#include <CoreFoundation/CoreFoundation.h>
#include <CoreFoundation/CFRuntime.h>
#include <CoreFoundation/CFPriv.h>
#include <CFNetwork/CFHTTPMessage.h>
#include <CFNetwork/CFHTTPMessagePriv.h>
#include <CFNetwork/CFHTTPStreamPriv.h>
#include "CFNetworkInternal.h"
#include "CFHTTPInternal.h"
#include <CFNetwork/CFHTTPStream.h>
#if defined(__MACH__)
#include <Security/cssm.h>
#include <mach-o/dyld.h>
#include "spnegoBlob.h"
#include "spnegoKrb.h"
#endif // __MACH__
struct __CFHTTPMessage {
CFRuntimeBase _cfBase;
CFStringRef _firstLine; CFStringRef _method;
CFURLRef _url;
CFMutableDictionaryRef _headers;
CFMutableArrayRef _headerOrder;
CFStringRef _lastKey; CFDataRef _data;
CFHTTPAuthenticationRef _auth;
CFHTTPAuthenticationRef _proxyAuth;
UInt32 _flags;
};
#define STATUS_MASK 0x000003FF
#define DELIMITER_MASK 0x00000C00
#define DELIMITER(flags) (((flags) & DELIMITER_MASK) >> 10)
#define DELIM_UNKNOWN 0
#define DELIM_CRLF 1
#define DELIM_CR 2
#define DELIM_LF 3
#define IS_RESPONSE 0x00001000
#define HEADERS_COMPLETE 0x00002000
#define MUTABLE_DATA 0x00004000
#define LAX_PARSING 0x00008000
#define IS_GET_METHOD 0x00010000
struct MessageHeaderMap {
const char _header[19];
int _length;
};
static const struct MessageHeaderMap kHTTPMessageHeaderMap[] = {
{"Accept-Ranges", 13},
{"Cache-Control", 13},
{"Connection", 10},
{"Content-Language", 16},
{"Content-Length", 14},
{"Content-Location", 16},
{"Content-Type", 12},
{"Date", 4},
{"Etag", 4},
{"Expires", 7},
{"Last-Modified", 13},
{"Location", 8},
{"Proxy-Authenticate", 18},
{"Server", 6},
{"Set-Cookie", 10}
};
#ifdef __CONSTANT_CFSTRINGS__
#define _kCFHTTPMessageAcceptRangesHeader CFSTR("Accept-Ranges")
#define _kCFHTTPMessageCacheControlHeader CFSTR("Cache-Control")
#define _kCFHTTPMessageConnectHeader CFSTR("Connection")
#define _kCFHTTPMessageContentLanguageHeader CFSTR("Content-Language")
#define _kCFHTTPMessageContentLengthHeader CFSTR("Content-Length")
#define _kCFHTTPMessageContentLocationHeader CFSTR("Content-Location")
#define _kCFHTTPMessageContentTypeHeader CFSTR("Content-Type")
#define _kCFHTTPMessageDateHeader CFSTR("Date")
#define _kCFHTTPMessageEtagHeader CFSTR("Etag")
#define _kCFHTTPMessageExpiresHeader CFSTR("Expires")
#define _kCFHTTPMessageLastModifiedHeader CFSTR("Last-Modified")
#define _kCFHTTPMessageLocationHeader CFSTR("Location")
#define _kCFHTTPMessageProxyAuthenticateHeader CFSTR("Proxy-Authenticate")
#define _kCFHTTPMessageServerHeader CFSTR("Server")
#define _kCFHTTPMessageSetCookieHeader CFSTR("Set-Cookie")
#else
static CONST_STRING_DECL(_kCFHTTPMessageAcceptRangesHeader, "Accept-Ranges")
static CONST_STRING_DECL(_kCFHTTPMessageCacheControlHeader, "Cache-Control")
static CONST_STRING_DECL(_kCFHTTPMessageConnectHeader, "Connection")
static CONST_STRING_DECL(_kCFHTTPMessageContentLanguageHeader, "Content-Language")
static CONST_STRING_DECL(_kCFHTTPMessageContentLengthHeader, "Content-Length")
static CONST_STRING_DECL(_kCFHTTPMessageContentLocationHeader, "Content-Location")
static CONST_STRING_DECL(_kCFHTTPMessageContentTypeHeader, "Content-Type")
static CONST_STRING_DECL(_kCFHTTPMessageDateHeader, "Date")
static CONST_STRING_DECL(_kCFHTTPMessageEtagHeader, "Etag")
static CONST_STRING_DECL(_kCFHTTPMessageExpiresHeader, "Expires")
static CONST_STRING_DECL(_kCFHTTPMessageLastModifiedHeader, "Last-Modified")
static CONST_STRING_DECL(_kCFHTTPMessageLocationHeader, "Location")
static CONST_STRING_DECL(_kCFHTTPMessageProxyAuthenticateHeader, "Proxy-Authenticate")
static CONST_STRING_DECL(_kCFHTTPMessageServerHeader, "Server")
static CONST_STRING_DECL(_kCFHTTPMessageSetCookieHeader, "Set-Cookie")
#endif
static const CFStringRef kHTTPMessageHeaderMap2[] = {
_kCFHTTPMessageAcceptRangesHeader,
_kCFHTTPMessageCacheControlHeader,
_kCFHTTPMessageConnectHeader,
_kCFHTTPMessageContentLanguageHeader,
_kCFHTTPMessageContentLengthHeader,
_kCFHTTPMessageContentLocationHeader,
_kCFHTTPMessageContentTypeHeader,
_kCFHTTPMessageDateHeader,
_kCFHTTPMessageEtagHeader,
_kCFHTTPMessageExpiresHeader,
_kCFHTTPMessageLastModifiedHeader,
_kCFHTTPMessageLocationHeader,
_kCFHTTPMessageProxyAuthenticateHeader,
_kCFHTTPMessageServerHeader,
_kCFHTTPMessageSetCookieHeader
};
#define kHTTPMessageNumItems (sizeof(kHTTPMessageHeaderMap) / sizeof(kHTTPMessageHeaderMap[0]))
#ifdef __CONSTANT_CFSTRINGS__
#define _kCFHTTPMessageDescribeFormat CFSTR("<CFHTTPMessage 0x%x>{url = %@; %@ = %@}")
#define _kCFHTTPMessageDescribeRequest CFSTR("request")
#define _kCFHTTPMessageDescribeStatus CFSTR("status")
#define _kCFHTTPMessageGETMethod CFSTR("GET")
#define _kCFHTTPMessageResponseLineFormat CFSTR(" %d ")
#define _kCFHTTPMessageSpace CFSTR(" ")
#define _kCFHTTPMessageEmptyString CFSTR("")
#define _kCFHTTPMessageAppendHeaderFormat CFSTR("%@, %@")
#else
static CONST_STRING_DECL(_kCFHTTPMessageDescribeFormat, "<CFHTTPMessage 0x%x>{url = %@; %@ = %@}")
static CONST_STRING_DECL(_kCFHTTPMessageDescribeRequest, "request")
static CONST_STRING_DECL(_kCFHTTPMessageDescribeStatus, "status")
static CONST_STRING_DECL(_kCFHTTPMessageGETMethod, "GET")
static CONST_STRING_DECL(_kCFHTTPMessageResponseLineFormat, " %d ")
static CONST_STRING_DECL(_kCFHTTPMessageSpace, " ")
static CONST_STRING_DECL(_kCFHTTPMessageEmptyString, "")
static CONST_STRING_DECL(_kCFHTTPMessageAppendHeaderFormat, "%@, %@")
#endif
static CFStringRef __CFHTTPMessageCopyDescription(CFTypeRef cf) {
CFHTTPMessageRef msg = (CFHTTPMessageRef)cf;
return CFStringCreateWithFormat(NULL, NULL, _kCFHTTPMessageDescribeFormat, msg, msg->_url, (msg->_flags & IS_RESPONSE) == 0 ? _kCFHTTPMessageDescribeRequest : _kCFHTTPMessageDescribeStatus, msg->_firstLine);
}
static void __CFHTTPMessageDeallocate(CFTypeRef cf) {
CFHTTPMessageRef req = (CFHTTPMessageRef)cf;
CFRelease(req->_headers);
CFRelease(req->_headerOrder);
if (req->_firstLine) CFRelease(req->_firstLine);
if (req->_method) CFRelease(req->_method);
if (req->_url) CFRelease(req->_url);
if (req->_data) CFRelease(req->_data);
if (req->_auth) CFRelease(req->_auth);
if (req->_proxyAuth) CFRelease(req->_proxyAuth);
if (req->_lastKey) CFRelease(req->_lastKey);
}
CONST_STRING_DECL(kCFHTTPVersion1_0, "HTTP/1.0")
CONST_STRING_DECL(kCFHTTPVersion1_1, "HTTP/1.1")
CONST_STRING_DECL(kCFHTTPRedirectionResponse,"kCFHTTPRedirectionResponse")
CONST_STRING_DECL(kCFStreamPropertyHTTPRequest,"kCFStreamPropertyHTTPRequest")
CONST_STRING_DECL(kCFStreamPropertyHTTPResponseHeader,"kCFStreamPropertyHTTPResponseHeader")
CONST_STRING_DECL(_kCFStreamPropertyHTTPPersistent, "_kCFStreamPropertyHTTPPersistent")
CONST_STRING_DECL(_kCFStreamPropertyHTTPNewHeader, "_kCFStreamPropertyHTTPNewHeader")
CONST_STRING_DECL(_kCFStreamPropertyHTTPLaxParsing, "_kCFStreamPropertyHTTPLaxParsing")
CONST_STRING_DECL(kCFStreamPropertyHTTPProxy, "kCFStreamPropertyHTTPProxy")
CONST_STRING_DECL(kCFStreamPropertyHTTPProxyHost, "HTTPProxy")
CONST_STRING_DECL(kCFStreamPropertyHTTPProxyPort, "HTTPPort")
CONST_STRING_DECL(kCFStreamPropertyHTTPSProxyHost, "HTTPSProxy")
CONST_STRING_DECL(kCFStreamPropertyHTTPSProxyPort, "HTTPSPort")
CONST_STRING_DECL(kCFStreamPropertyHTTPShouldAutoredirect, "kCFStreamPropertyHTTPShouldAutoredirect")
CONST_STRING_DECL(kCFStreamPropertyHTTPFinalURL, "kCFStreamPropertyHTTPFinalURL")
CONST_STRING_DECL(kCFStreamPropertyHTTPAttemptPersistentConnection, "kCFStreamPropertyHTTPAttemptPersistentConnection")
CONST_STRING_DECL(kCFStreamPropertyHTTPRequestBytesWrittenCount, "kCFStreamPropertyHTTPRequestBytesWrittenCount")
CONST_STRING_DECL(_kCFStreamPropertyHTTPConnectionStreams, "_kCFStreamPropertyHTTPConnectionStreams")
CONST_STRING_DECL(_kCFStreamPropertyHTTPZeroLengthResponseExpected, "_kCFStreamPropertyHTTPZeroLengthResponseExpected")
CONST_STRING_DECL(_kCFStreamPropertyHTTPProxyProxyAutoConfigURLString, "ProxyAutoConfigURLString")
CONST_STRING_DECL(_kCFStreamPropertyHTTPProxyProxyAutoConfigEnable, "ProxyAutoConfigEnable")
CONST_STRING_DECL(_kCFStreamPropertyHTTPConnection, "_kCFStreamPropertyHTTPConnection")
static _CFOnceLock gHTTPMessageClassRegistration = _CFOnceInitializer;
static CFTypeID __kCFHTTPMessageTypeID = _kCFRuntimeNotATypeID;
static void
_HTTPMessageRegisterClass(void) {
static const CFRuntimeClass __CFHTTPMessageClass = {
0,
"CFHTTPMessage",
NULL, NULL, __CFHTTPMessageDeallocate,
NULL, NULL, NULL, __CFHTTPMessageCopyDescription
};
#if defined(__WIN32__)
int i;
for (i = 0; i < kHTTPMessageNumItems; i++)
kHTTPMessageHeaderMap[i]._constant = CFStringCreateWithCStringNoCopy(NULL, kHTTPMessageHeaderMap[i]._header, kCFStringEncodingASCII, kCFAllocatorNull);
#endif
__kCFHTTPMessageTypeID = _CFRuntimeRegisterClass(&__CFHTTPMessageClass);
}
CFTypeID CFHTTPMessageGetTypeID(void) {
_CFDoOnce(&gHTTPMessageClassRegistration, _HTTPMessageRegisterClass);
return __kCFHTTPMessageTypeID;
}
#if defined(__WIN32__)
extern void _CFHTTPMessageCleanup(void) {
if (__kCFHTTPMessageTypeID != _kCFRuntimeNotATypeID) {
int i;
for (i = 0; i < kHTTPMessageNumItems; i++) {
CFRelease(kHTTPMessageHeaderMap[i]._constant);
kHTTPMessageHeaderMap[i]._constant = NULL;
}
}
}
#endif
static CFHTTPMessageRef _CFHTTPMessageCreate(CFAllocatorRef allocator) {
struct __CFHTTPMessage *newMsg;
newMsg = (struct __CFHTTPMessage *)_CFRuntimeCreateInstance(allocator, CFHTTPMessageGetTypeID(), sizeof(struct __CFHTTPMessage) - sizeof(CFRuntimeBase), NULL);
if (newMsg) {
newMsg->_firstLine = NULL;
newMsg->_method = NULL;
newMsg->_url = NULL;
newMsg->_headers = CFDictionaryCreateMutable(allocator, 17, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
newMsg->_headerOrder = CFArrayCreateMutable(allocator, 17, &kCFTypeArrayCallBacks);
newMsg->_lastKey = NULL;
newMsg->_data = NULL;
newMsg->_auth = NULL;
newMsg->_proxyAuth = NULL;
newMsg->_flags = LAX_PARSING; }
return newMsg;
}
CFHTTPMessageRef CFHTTPMessageCreateCopy(CFAllocatorRef allocator, CFHTTPMessageRef msg) {
struct __CFHTTPMessage *result;
result = (struct __CFHTTPMessage *)_CFRuntimeCreateInstance(allocator, CFHTTPMessageGetTypeID(), sizeof(struct __CFHTTPMessage) - sizeof(CFRuntimeBase), NULL);
if (result) {
result->_firstLine = msg->_firstLine ? CFStringCreateCopy(allocator, msg->_firstLine) : NULL;
result->_method = msg->_method ? CFRetain(msg->_method) : NULL;
result->_url = msg->_url ? CFRetain(msg->_url) : NULL;
result->_headers = CFDictionaryCreateMutableCopy(allocator, (CFDictionaryGetCount(msg->_headers) >= 16 ? 0 : 16), msg->_headers);
result->_headerOrder = CFArrayCreateMutableCopy(allocator, (CFArrayGetCount(msg->_headerOrder) >= 16 ? 0 : 16), msg->_headerOrder);
result->_flags = msg->_flags;
result->_lastKey = msg->_lastKey ? CFRetain(msg->_lastKey) : NULL;
if (msg->_data == NULL) {
result->_data = NULL;
} else if ((msg->_flags & MUTABLE_DATA) == 0) {
result->_data = CFRetain(msg->_data);
} else {
result->_data = CFDataCreateMutableCopy(allocator, 0, msg->_data);
}
result->_auth = msg->_auth;
result->_proxyAuth = msg->_proxyAuth;
if (result->_auth)
CFRetain(msg->_auth);
if (result->_proxyAuth)
CFRetain(msg->_proxyAuth);
}
return result;
}
UInt8 *_CFURLPortionForRequest(CFAllocatorRef alloc, CFURLRef url, Boolean useCompleteURL, UInt8 **buf, CFIndex bufLength, Boolean *deallocateBuffer) {
CFURLRef absURL = CFURLCopyAbsoluteURL(url);
UInt8 *urlBytes;
CFIndex length;
CFRange urlRg;
CFRange fragRg;
Boolean prependedSlash = FALSE;
*deallocateBuffer = FALSE;
length = CFURLGetBytes(absURL, (*buf) + 1, bufLength-2);
if (length == -1) {
*deallocateBuffer = TRUE;
length = CFURLGetBytes(absURL, NULL, 0);
*buf = CFAllocatorAllocate(alloc, length+2, 0);
CFURLGetBytes(absURL, (*buf)+1, length);
}
urlBytes = (*buf)+1;
if (!useCompleteURL) {
CFRange pathWithSeparators;
urlRg = CFURLGetByteRangeForComponent(absURL, kCFURLComponentPath, &pathWithSeparators);
if (!urlRg.location == kCFNotFound || urlRg.length == 0) {
*(urlBytes + pathWithSeparators.location - 1) = '/';
prependedSlash = TRUE;
urlRg = pathWithSeparators;
}
} else {
urlRg.location = 0;
}
fragRg = CFURLGetByteRangeForComponent(absURL, kCFURLComponentFragment, NULL);
if (fragRg.location == -1) {
urlRg.length = length - urlRg.location;
} else {
urlRg.length = fragRg.location - 1 - urlRg.location;
}
*(urlBytes+urlRg.location+urlRg.length) = '\0';
CFRelease(absURL);
return prependedSlash ? urlBytes + urlRg.location - 1 : urlBytes + urlRg.location;
}
static CFStringRef createRequestLine(CFAllocatorRef alloc, CFStringRef method, CFURLRef url, CFStringRef version, Boolean useCompleteURL) {
CFMutableStringRef line;
UInt8 buf[512], *urlBytes = buf;
UInt8 *urlPortion;
Boolean freeBytes = FALSE;
line = CFStringCreateMutableCopy(alloc, 0, method);
CFStringAppendCString(line, " ", kCFStringEncodingASCII);
urlPortion = _CFURLPortionForRequest(alloc, url, useCompleteURL, &urlBytes, sizeof(buf)/sizeof(UInt8), &freeBytes);
CFStringAppendCString(line, (const char*)urlPortion, kCFStringEncodingISOLatin1);
if (freeBytes) CFAllocatorDeallocate(alloc, urlBytes);
CFStringAppendCString(line, " ", kCFStringEncodingASCII);
CFStringAppend(line, version);
return line;
}
CFHTTPMessageRef CFHTTPMessageCreateRequest(CFAllocatorRef allocator, CFStringRef requestMethod, CFURLRef url, CFStringRef httpVersion) {
CFHTTPMessageRef newReq;
if (!requestMethod || !url) {
return NULL;
}
newReq = _CFHTTPMessageCreate(allocator);
if (newReq) {
newReq->_firstLine = createRequestLine(allocator, requestMethod, url, httpVersion, FALSE);
newReq->_method = CFStringCreateCopy(allocator, requestMethod);
if (CFStringCompare(requestMethod, _kCFHTTPMessageGETMethod, kCFCompareCaseInsensitive) == kCFCompareEqualTo)
newReq->_flags |= IS_GET_METHOD;
CFRetain(url);
newReq->_url = url;
}
return newReq;
}
CF_INLINE const char *descriptionForResponseCode(int code) {
switch (code) {
case 100: return "Continue";
case 101: return "Switching Protocols";
case 200: return "OK";
case 201: return "Created";
case 202: return "Accepted";
case 203: return "Non-Authoritative Information";
case 204: return "No Content";
case 205: return "Reset Content";
case 206: return "Partial Content";
case 300: return "Multiple Choices";
case 301: return "Moved Permanently";
case 302: return "Found";
case 303: return "See Other";
case 304: return "Not Modified";
case 305: return "Use Proxy";
case 307: return "Temporary Redirect";
case 400: return "Bad Request";
case 401: return "Unauthorized";
case 402: return "Payment Required";
case 403: return "Forbidden";
case 404: return "Not Found";
case 405: return "Method Not Allowed";
case 406: return "Not Acceptable";
case 407: return "Proxy Authentication Required";
case 408: return "Request Time-out";
case 409: return "Conflict";
case 410: return "Gone";
case 411: return "Length Required";
case 412: return "Precondition Failed";
case 413: return "Request Entity Too Large";
case 414: return "Request-URI Too Large";
case 415: return "Unsupported Media Type";
case 416: return "Requested range not satisfiable";
case 417: return "Expectation Failed";
case 500: return "Internal Server Error";
case 501: return "Not Implemented";
case 502: return "Bad Gateway";
case 503: return "Service Unavailable";
case 504: return "Gateway Time-out";
case 505: return "HTTP Version not supported";
default:
if (code < 200) {
return "Continue";
} else if (code < 300) {
return "OK";
} else if (code < 400) {
return "Multiple Choices";
} else if (code < 500) {
return "Bad Request";
} else {
return "Internal Server Error";
}
}
}
static CFStringRef createResponseLine(CFAllocatorRef alloc, int code, CFStringRef desc, CFStringRef version) {
CFMutableStringRef line = CFStringCreateMutableCopy(alloc, 0, version);
CFStringAppendFormat(line, NULL, _kCFHTTPMessageResponseLineFormat, code);
if (desc) {
CFStringAppend(line, desc);
} else {
CFStringAppendCString(line, descriptionForResponseCode(code), kCFStringEncodingASCII);
}
return line;
}
CFHTTPMessageRef CFHTTPMessageCreateResponse(CFAllocatorRef allocator, int statusCode, CFStringRef statusDescription, CFStringRef httpVersion) {
CFHTTPMessageRef newResponse;
newResponse = _CFHTTPMessageCreate(allocator);
if (newResponse) {
newResponse->_flags |= IS_RESPONSE;
newResponse->_flags = (newResponse->_flags & ~STATUS_MASK) | (statusCode & STATUS_MASK);
newResponse->_firstLine = createResponseLine(allocator, statusCode, statusDescription, httpVersion);
}
return newResponse;
}
CFHTTPMessageRef CFHTTPMessageCreateEmpty(CFAllocatorRef allocator, Boolean isRequest) {
CFHTTPMessageRef newMsg = _CFHTTPMessageCreate(allocator);
if (newMsg && !isRequest) {
newMsg->_flags |= IS_RESPONSE;
}
return newMsg;
}
extern void _CFHTTPMessageSetLaxParsing(CFHTTPMessageRef msg, Boolean allowLaxParsing) {
if (allowLaxParsing) {
msg->_flags |= LAX_PARSING;
} else {
msg->_flags &= (~LAX_PARSING);
}
}
Boolean CFHTTPMessageIsRequest(CFHTTPMessageRef message) {
return (message->_flags & IS_RESPONSE) ? FALSE : TRUE;
}
CFHTTPAuthenticationRef _CFHTTPMessageGetAuthentication(CFHTTPMessageRef message, Boolean proxy) {
return proxy ? message->_proxyAuth : message->_auth;
}
void _CFHTTPMessageSetAuthentication(CFHTTPMessageRef message, CFHTTPAuthenticationRef auth, Boolean proxy) {
CFHTTPAuthenticationRef* ptr = proxy ? &(message->_proxyAuth) : &(message->_auth);
CFRetain(auth);
if (*ptr)
CFRelease(*ptr);
*ptr = auth;
}
CFStringRef CFHTTPMessageCopyVersion(CFHTTPMessageRef message) {
CFRange rg = {0, 0};
if (!message->_firstLine) {
return NULL;
}
if (message->_flags & IS_RESPONSE) {
if (CFStringFindWithOptions(message->_firstLine, _kCFHTTPMessageSpace, CFRangeMake(0, CFStringGetLength(message->_firstLine)), 0, &rg)) {
rg.length = rg.location;
rg.location = 0;
} else {
rg.location = 0;
rg.length = 0;
}
} else {
if (CFStringFindWithOptions(message->_firstLine, _kCFHTTPMessageSpace, CFRangeMake(0, CFStringGetLength(message->_firstLine)), kCFCompareBackwards, &rg)) {
rg.location ++;
rg.length = CFStringGetLength(message->_firstLine) - rg.location;
} else {
rg.location = 0;
rg.length = 0;
}
}
if (rg.length > 0) {
return CFStringCreateWithSubstring(CFGetAllocator(message), message->_firstLine, rg);
} else {
return NULL;
}
}
CFDataRef CFHTTPMessageCopyBody(CFHTTPMessageRef msg) {
if (msg->_data) {
if ((msg->_flags & MUTABLE_DATA) == 0) {
CFRetain(msg->_data);
return msg->_data;
} else {
return CFDataCreateCopy(CFGetAllocator(msg), msg->_data);
}
} else {
return NULL;
}
}
void CFHTTPMessageSetBody(CFHTTPMessageRef msg, CFDataRef data) {
msg->_flags &= (~MUTABLE_DATA);
if (data) {
data = CFDataCreateCopy(CFGetAllocator(msg), data);
}
if (msg->_data) {
CFRelease(msg->_data);
}
msg->_data = data;
}
CFStringRef _CFCapitalizeHeader(CFStringRef headerString) {
CFIndex i, len = CFStringGetLength(headerString);
CFAllocatorRef alloc = CFGetAllocator(headerString);
UInt8 *charPtr = NULL;
UniChar *uniCharPtr = NULL;
Boolean useUniCharPtr = FALSE;
Boolean shouldCapitalize = TRUE;
Boolean somethingChanged = FALSE;
for (i = 0; i < len; i ++) {
UniChar ch = CFStringGetCharacterAtIndex(headerString, i);
Boolean replace = FALSE;
if (shouldCapitalize && ch >= 'a' && ch <= 'z') {
ch = ch + 'A' - 'a';
replace = TRUE;
} else if (!shouldCapitalize && ch >= 'A' && ch <= 'Z') {
ch = ch + 'a' - 'A';
replace = TRUE;
}
if (replace) {
if (!somethingChanged) {
CFIndex converted = 0;
somethingChanged = TRUE;
charPtr = _CFStringGetOrCreateCString(alloc, headerString, NULL, &converted, kCFStringEncodingISOLatin1);
if (converted == len) {
useUniCharPtr = FALSE;
} else {
useUniCharPtr = TRUE;
CFAllocatorDeallocate(alloc, charPtr);
uniCharPtr = (UniChar*)_CFStringGetOrCreateCString(alloc, headerString, NULL, &converted, kCFStringEncodingUnicode);
}
}
if (useUniCharPtr) {
uniCharPtr[i] = ch;
} else {
charPtr[i] = ch;
}
}
if (ch == '-') {
shouldCapitalize = TRUE;
} else {
shouldCapitalize = FALSE;
}
}
if (somethingChanged) {
if (useUniCharPtr) {
return CFStringCreateWithCharactersNoCopy(alloc, uniCharPtr, len, alloc);
} else {
return CFStringCreateWithCStringNoCopy(alloc, (const char*)charPtr, kCFStringEncodingISOLatin1, alloc);
}
} else {
CFRetain(headerString);
return headerString;
}
}
CFStringRef CFHTTPMessageCopyHeaderFieldValue(CFHTTPMessageRef msg, CFStringRef header) {
CFStringRef lowerHeader = _CFCapitalizeHeader(header);
CFStringRef result = CFDictionaryGetValue(msg->_headers, lowerHeader);
CFRelease(lowerHeader);
if (result) CFRetain(result);
return result;
}
CFDictionaryRef CFHTTPMessageCopyAllHeaderFields(CFHTTPMessageRef msg) {
CFRetain(msg->_headers);
return msg->_headers;
}
extern void _CFHTTPMessageSetHeader(CFHTTPMessageRef msg, CFStringRef header, CFStringRef value, CFIndex position) {
if (!value) {
CFDictionaryRemoveValue(msg->_headers, header);
CFArrayRemoveValueAtIndex(msg->_headerOrder,
CFArrayGetFirstIndexOfValue(msg->_headerOrder, CFRangeMake(0, CFArrayGetCount(msg->_headerOrder)),
header));
}
else {
if (!CFDictionaryContainsKey(msg->_headers, header)) {
CFIndex count = CFArrayGetCount(msg->_headerOrder);
CFRange rg = CFRangeMake(((position >= 0) && (position < count)) ? position : count, 0);
if (count == 16) {
CFTypeRef temp = CFArrayCreateMutableCopy(CFGetAllocator(msg), 0, msg->_headerOrder);
CFRelease(msg->_headerOrder);
msg->_headerOrder = (CFMutableArrayRef)temp;
temp = CFDictionaryCreateMutableCopy(CFGetAllocator(msg), 0, msg->_headers);
CFRelease(msg->_headers);
msg->_headers = (CFMutableDictionaryRef)temp;
}
CFArrayReplaceValues(msg->_headerOrder, rg, (const void**)(&header), 1);
}
CFDictionarySetValue(msg->_headers, header, value);
}
}
void CFHTTPMessageSetHeaderFieldValue(CFHTTPMessageRef message, CFStringRef headerField, CFStringRef value) {
CFStringRef header = _CFCapitalizeHeader(headerField);
_CFHTTPMessageSetHeader(message, header, value, -1);
CFRelease(header);
}
extern CFDataRef _CFHTTPMessageCopySerializedHeaders(CFHTTPMessageRef msg, Boolean forProxy);
extern CFDataRef _CFHTTPMessageCopySerializedHeaders(CFHTTPMessageRef msg, Boolean forProxy) {
CFAllocatorRef allocator = CFGetAllocator(msg);
CFMutableStringRef headers;
CFDataRef result;
unsigned i,c;
if ((msg->_flags & IS_RESPONSE) != 0 || !forProxy) {
headers = CFStringCreateMutableCopy(allocator, 0, msg->_firstLine);
} else {
CFStringRef method = CFHTTPMessageCopyRequestMethod(msg);
CFStringRef version = CFHTTPMessageCopyVersion(msg);
CFStringRef line = createRequestLine(allocator, method, msg->_url, version, forProxy);
CFRelease(method);
CFRelease(version);
headers = CFStringCreateMutableCopy(allocator, 0, line);
CFRelease(line);
}
CFStringAppendCString(headers, "\r\n", kCFStringEncodingASCII);
for (i = 0, c = CFArrayGetCount(msg->_headerOrder); i < c; i ++) {
CFStringRef header = CFArrayGetValueAtIndex(msg->_headerOrder, i);
CFStringAppend(headers, header);
CFStringAppendCString(headers, ": ", kCFStringEncodingASCII);
CFStringAppend(headers, CFDictionaryGetValue(msg->_headers, header));
CFStringAppendCString(headers, "\r\n", kCFStringEncodingASCII);
}
CFStringAppendCString(headers, "\r\n", kCFStringEncodingASCII);
result = CFStringCreateExternalRepresentation(allocator, headers, kCFStringEncodingISOLatin1, '?');
CFRelease(headers);
return result;
}
extern CFDataRef _CFHTTPMessageCopySerializedMessage(CFHTTPMessageRef msg, Boolean forProxy);
extern CFDataRef _CFHTTPMessageCopySerializedMessage(CFHTTPMessageRef msg, Boolean forProxy) {
CFDataRef result = _CFHTTPMessageCopySerializedHeaders(msg, forProxy);
if (msg->_data) {
CFMutableDataRef hdrData = CFDataCreateMutableCopy(CFGetAllocator(msg), CFDataGetLength(msg->_data) + CFDataGetLength(result), result);
CFRelease(result);
CFDataAppendBytes(hdrData, CFDataGetBytePtr(msg->_data), CFDataGetLength(msg->_data));
result = hdrData;
}
return result;
}
CFDataRef CFHTTPMessageCopySerializedMessage(CFHTTPMessageRef msg) {
return _CFHTTPMessageCopySerializedMessage(msg, FALSE);
}
Boolean CFHTTPMessageIsHeaderComplete(CFHTTPMessageRef message) {
return (message->_flags & HEADERS_COMPLETE) == 0 ? FALSE : TRUE;
}
Boolean _CFHTTPMessageIsGetMethod(CFHTTPMessageRef msg) {
if (!msg->_method) {
CFStringRef method = CFHTTPMessageCopyRequestMethod(msg);
if (method)
CFRelease(method);
}
return (msg->_flags & IS_GET_METHOD) ? TRUE : FALSE;
}
CFStringRef CFHTTPMessageCopyRequestMethod(CFHTTPMessageRef request) {
if (!request->_method) {
CFRange rg;
if (request->_firstLine && CFStringFindWithOptions(request->_firstLine, _kCFHTTPMessageSpace, CFRangeMake(0, CFStringGetLength(request->_firstLine)), 0, &rg)) {
rg.length = rg.location;
rg.location = 0;
request->_method = CFStringCreateWithSubstring(CFGetAllocator(request), request->_firstLine, rg);
if (request->_method && (CFStringCompare(request->_method, _kCFHTTPMessageGETMethod, kCFCompareCaseInsensitive) == kCFCompareEqualTo))
request->_flags |= IS_GET_METHOD;
}
}
if (request->_method)
return CFRetain(request->_method);
return NULL;
}
CFURLRef CFHTTPMessageCopyRequestURL(CFHTTPMessageRef request) {
CFRetain(request->_url);
return request->_url;
}
extern
void _CFHTTPMessageSetResponseURL(CFHTTPMessageRef response, CFURLRef url) {
CFRetain(url);
if (response->_url) CFRelease(response->_url);
response->_url = url;
}
UInt32 CFHTTPMessageGetResponseStatusCode(CFHTTPMessageRef response) {
if (!response->_firstLine) {
return -1;
} else if (CFStringGetLength(response->_firstLine) == 0) {
return 200;
} else {
return (response->_flags & STATUS_MASK);
}
}
CFStringRef CFHTTPMessageCopyResponseStatusLine(CFHTTPMessageRef response) {
if (response->_firstLine) {
CFRetain(response->_firstLine);
}
return response->_firstLine;
}
static const UInt8 *parseHTTPVersion(const UInt8 *bytes, CFIndex len, Boolean consumeSpaces) {
Boolean sawDecimal = FALSE, sawOneDigit = FALSE;
const UInt8 *currentByte, *lastByte = bytes + len;
if (len < 8) {
if (len > 0 && bytes[0] != 'H') return NULL;
return bytes;
} else if (!(bytes[0] == 'H' && bytes[1] == 'T' && bytes[2] == 'T' && bytes[3] == 'P' && bytes[4] == '/')) {
return NULL;
}
for (currentByte = bytes+5; currentByte < lastByte; currentByte ++) {
UInt8 ch = *currentByte;
if (ch <= '9' && ch >= '0') {
sawOneDigit = TRUE;
} else if (ch == '.') {
if (sawDecimal) {
return sawOneDigit ? currentByte : NULL;
} else {
sawDecimal = TRUE;
sawOneDigit = FALSE;
}
} else {
if (sawDecimal && sawOneDigit) {
if (consumeSpaces) {
while (currentByte < lastByte && *currentByte == ' ') {
currentByte ++;
}
}
return currentByte;
} else {
return NULL;
}
}
}
return bytes;
}
static const UInt8 *_extractResponseStatusLine(CFHTTPMessageRef response, const UInt8 *bytes, CFIndex len) {
const UInt8 *currentByte = parseHTTPVersion(bytes, len, TRUE);
const UInt8 *end = bytes + len;
if (currentByte == bytes || currentByte + 3 >= end) { return bytes;
} else if (currentByte == NULL || *currentByte > '9' || *currentByte < '0' || currentByte[1] > '9' || currentByte[1] < '0' || currentByte[2] > '9' || currentByte[2] < '0') {
response->_firstLine = CFRetain(_kCFHTTPMessageEmptyString);
response->_flags |= HEADERS_COMPLETE;
return bytes;
} else {
UInt32 delim = DELIM_UNKNOWN;
UInt32 status = (currentByte[0] - '0')*100 + (currentByte[1] - '0')*10 + (currentByte[2] - '0');
currentByte += 3;
while (currentByte < end) {
if (*currentByte == '\n' || *currentByte == '\r') {
break;
}
currentByte ++;
}
if (currentByte < end) {
if (*currentByte == '\n') {
delim = DELIM_LF;
} else if (currentByte+1 < end) {
delim = (*(currentByte+1) == '\n') ? DELIM_CRLF : DELIM_CR;
}
}
if (delim == DELIM_UNKNOWN) {
return bytes;
} else {
response->_firstLine = CFStringCreateWithBytes(CFGetAllocator(response), bytes, currentByte - bytes, kCFStringEncodingISOLatin1, FALSE);
response->_flags = (response->_flags & ~DELIMITER_MASK) | (delim << 10);
response->_flags = (response->_flags & ~STATUS_MASK) | status;
return (delim == DELIM_CRLF) ? currentByte + 2 : currentByte + 1;
}
}
}
static const UInt8 *_extractRequestFirstLine(CFHTTPMessageRef request, const UInt8 *bytes, CFIndex len) {
const UInt8 *end = bytes + len;
const UInt8 *current;
const UInt8 *methodEnd = NULL, *urlEnd = NULL, *versionEnd = NULL;
Boolean fail = FALSE, incomplete = FALSE;
UInt32 delim = DELIM_CRLF;
for (current = bytes; current < end; current ++) {
UInt8 ch = *current;
if (ch > 126) break;
if (ch < 32) break;
if (ch == '(' || ch == ')' || ch == '<' || ch == '>' || ch == '@' || ch == ',' || ch == ';' || ch == ':' || ch == '\\' || ch == '\"' || ch == '/' || ch == '[' || ch == ']' || ch == '?' || ch == '=' || ch == '{' || ch == '}' ) break;
if (ch == ' ') {
methodEnd = current;
current ++;
break;
}
}
if (methodEnd == NULL) {
if (current == end) { return bytes;
} else {
return NULL; }
}
while (current < end) {
if (*current == ' ') break;
current ++;
}
if (current < end) {
urlEnd = current;
current ++;
request->_url = CFURLCreateWithBytes(CFGetAllocator(request), methodEnd+1, urlEnd - methodEnd - 1, kCFStringEncodingUTF8, NULL);
if (!request->_url) {
return NULL;
}
} else {
return bytes;
}
versionEnd = parseHTTPVersion(current, end-current, FALSE);
if (!versionEnd) { fail = TRUE;
} else if (versionEnd == current || versionEnd == end) { incomplete = TRUE;
} else if (*versionEnd != '\r' && *versionEnd != '\n') {
fail = TRUE;
} else {
if (*versionEnd == '\n') {
delim = DELIM_LF;
} else if (versionEnd < end) {
if (versionEnd[1] == '\n') {
delim = DELIM_CRLF;
} else {
delim = DELIM_CR;
}
} else {
incomplete = TRUE;
}
}
if (fail || incomplete) {
CFRelease(request->_url);
request->_url = NULL;
return fail ? NULL : bytes;
} else {
request->_firstLine = CFStringCreateWithBytes(CFGetAllocator(request), bytes, versionEnd - bytes, kCFStringEncodingISOLatin1, FALSE);
request->_flags = (request->_flags & ~DELIMITER_MASK) | (delim << 10);
return (delim == DELIM_CRLF) ? versionEnd + 2 : versionEnd + 1;
}
}
static inline const UInt8 *_findEOL(CFHTTPMessageRef response, const UInt8 *bytes, CFIndex len) {
const UInt8* result = memchr(bytes, '\n', len);
if (!result)
result = memchr(bytes, '\r', len - 1);
return result;
}
static Boolean _parseHeadersFromData(CFHTTPMessageRef message) {
Boolean result = TRUE;
CFAllocatorRef alloc = CFGetAllocator(message);
const UInt8* start = CFDataGetBytePtr(message->_data);
const UInt8* end = start + CFDataGetLength(message->_data);
if (!message->_firstLine) {
const UInt8* newStart;
if (message->_flags & IS_RESPONSE)
newStart = _extractResponseStatusLine(message, start, end - start);
else
newStart = _extractRequestFirstLine(message, start, end - start);
if (newStart == start)
return TRUE;
if (!newStart)
return FALSE;
start = newStart;
}
while ((start != end) && !(message->_flags & HEADERS_COMPLETE)) {
UInt8 c;
const UInt8* eov; const UInt8* eol = _findEOL(message, start, end - start);
if (!eol)
break;
eov = eol - 1;
if ((*eov == '\r') && (*eol == '\n'))
eov--;
if (start >= eov) {
start = eol + 1;
message->_flags |= HEADERS_COMPLETE;
if (message->_lastKey) {
CFRelease(message->_lastKey);
message->_lastKey = NULL;
}
break;
}
c = *start;
if ((c == ' ') || (c == '\t')) {
if (!message->_lastKey) {
if (!(message->_flags & LAX_PARSING)) {
result = FALSE;
break;
}
}
else {
CFMutableStringRef value = CFStringCreateMutableCopy(alloc, 0, CFDictionaryGetValue(message->_headers, message->_lastKey));
CFStringRef str = CFStringCreateWithBytes(alloc, start, eov - start + 1, kCFStringEncodingISOLatin1, FALSE);
CFStringAppend(value, str);
CFRelease(str);
CFHTTPMessageSetHeaderFieldValue(message, message->_lastKey, value);
CFRelease(value);
}
}
else {
const UInt8* colon = memchr(start, ':', eol - start);
if (!colon) {
if (!(message->_flags & LAX_PARSING))
{
result = FALSE;
break;
}
}
else {
CFStringRef key = NULL;
CFStringRef value, old;
int i;
for (i = 0; i < kHTTPMessageNumItems; i++) {
const struct MessageHeaderMap* map = &kHTTPMessageHeaderMap[i];
if (((colon - start) == map->_length) &&
!strncmp(map->_header, (const char*)start, map->_length))
{
key = CFRetain(kHTTPMessageHeaderMap2[i]);
break;
}
}
if (!key) {
CFStringRef temp;
key = CFStringCreateWithBytes(alloc, start, colon - start, kCFStringEncodingISOLatin1, FALSE);
temp = _CFCapitalizeHeader(key);
CFRelease(key);
key = temp;
}
if (message->_lastKey)
CFRelease(message->_lastKey);
message->_lastKey = CFRetain(key);
c = *++colon;
while ((c == ' ') || (c == '\t'))
c = *++colon;
if (colon > eov)
value = CFRetain(_kCFHTTPMessageEmptyString);
else
value = CFStringCreateWithBytes(alloc, colon, eov - colon + 1, kCFStringEncodingISOLatin1, FALSE);
old = CFDictionaryGetValue(message->_headers, key);
if (old) {
CFStringRef newValue = CFStringCreateWithFormat(alloc, NULL, _kCFHTTPMessageAppendHeaderFormat, old, value);
CFRelease(value);
value = newValue;
}
_CFHTTPMessageSetHeader(message, key, value, -1);
CFRelease(key);
CFRelease(value);
}
}
start = eol + 1;
}
if (start != CFDataGetBytePtr(message->_data)) {
if (message->_flags & MUTABLE_DATA) {
CFDataReplaceBytes((CFMutableDataRef)message->_data, CFRangeMake(0, start - CFDataGetBytePtr(message->_data)), NULL, 0);
}
else {
message->_flags &= (~MUTABLE_DATA);
CFRelease(message->_data);
message->_data = CFDataCreate(alloc, start, end - start);
}
}
return result;
}
Boolean CFHTTPMessageAppendBytes(CFHTTPMessageRef message, const UInt8 *newBytes, CFIndex numBytes) {
if (numBytes == 0) {
return TRUE;
}
if (message->_data == NULL) {
message->_data = CFDataCreateMutable(CFGetAllocator(message), 0);
message->_flags |= MUTABLE_DATA;
} else if ((message->_flags & MUTABLE_DATA) == 0) {
CFMutableDataRef newData = CFDataCreateMutableCopy(CFGetAllocator(message), 0, message->_data);
CFRelease(message->_data);
message->_data = newData;
message->_flags |= MUTABLE_DATA;
}
CFDataAppendBytes((CFMutableDataRef)message->_data, newBytes, numBytes);
if ((message->_flags & HEADERS_COMPLETE) == 0) {
return _parseHeadersFromData(message);
}
return TRUE;
}
extern Boolean _CFHTTPMessageConvertToDataOnlyResponse(CFHTTPMessageRef message) {
if (message->_firstLine) return FALSE;
if (!(message->_flags & IS_RESPONSE)) return FALSE;
message->_firstLine = CFRetain(_kCFHTTPMessageEmptyString);
message->_flags |= HEADERS_COMPLETE;
return TRUE;
}
extern Boolean _CFHTTPMessageCanStandAlone(CFHTTPMessageRef message) {
if (!(message->_flags & IS_RESPONSE)) return FALSE;
if (message->_flags & HEADERS_COMPLETE) return TRUE;
if (!message->_firstLine) return FALSE;
if (message->_data && !CFDataGetLength(message->_data)) {
message->_flags |= HEADERS_COMPLETE;
return TRUE;
}
return FALSE;
}
extern CFDataRef _CFHTTPMessageGetBody(CFHTTPMessageRef msg) {
return msg->_data;
}
extern Boolean _CFHTTPMessageIsEmpty(CFHTTPMessageRef message) {
if (message->_firstLine) return FALSE;
if (message->_data && CFDataGetLength(message->_data)) return FALSE;
return TRUE;
}