#include <CFNetwork/CFHTTPStreamPriv.h>
#include <CFNetwork/CFHTTPMessagePriv.h>
#include "CFNetConnection.h"
#include "CFNetworkInternal.h"
#include "CFHTTPInternal.h"
#include <CFNetwork/CFHTTPConnectionPriv.h>
#include <CoreFoundation/CFStreamPriv.h>
#include <CoreFoundation/CFBundlePriv.h>
#include <CFNetwork/CFSocketStreamPriv.h>
#if defined(__MACH__)
#include <SystemConfiguration/SCSchemaDefinitions.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#endif
#if defined(__WIN32__)
#define usleep(usec) Sleep(1.0 > rintf(((float)(usec))/1000.0)) ? 1.0 : rintf(((float)(usec))/1000.0)))
#include <winsock2.h>
#define ECONNRESET WSAECONNRESET
#endif
#define BUF_SIZE (2048)
#define HAVE_SENT_REQUEST_HEADERS (0)
#define HAVE_SENT_REQUEST_PAYLOAD (1)
#define HAVE_CHECKED_RESPONSE_HEADERS (2)
#define FORCE_EOF (3)
#define AUTOREDIRECT (4)
#define PAYLOAD_IS_DATA (5)
#define OPEN_SIGNALLED (6)
#define IS_PERSISTENT (7)
#define MIN_STATE_BIT (8)
#define MAX_STATE_BIT (11)
#define HAS_PAYLOAD (12)
#define IS_ZOMBIE (14)
#define IN_READ_CALLBACK (16)
#define CUSTOM_STREAMS (17)
#define WAITING_FOR_PROXY_STREAM (18)
#define DO_NOT_REATTEMPT (19)
#define HAVE_READ_MARK (20)
typedef struct _CFHTTPRequest {
CFOptionFlags flags;
CFHTTPMessageRef originalRequest, currentRequest;
CFHTTPMessageRef responseHeaders; CFReadStreamRef requestPayload; CFDataRef requestFragment; long long requestBytesWritten;
CFReadStreamRef responseStream;
CFDictionaryRef proxyDict;
CFMutableArrayRef proxyList;
CFReadStreamRef proxyStream;
CFMutableArrayRef redirectedURLs; CFHTTPMessageRef firstRedirection;
_CFNetConnectionRef conn; CFRunLoopSourceRef stateChangeSource; CFMutableDictionaryRef connProps;
CFArrayRef peerCertificates;
} _CFHTTPRequest;
struct _CFHTTPTestSOCKSContext {
CFDictionaryRef dict;
Boolean isGood;
};
static _CFNetConnectionCacheKey nextConnectionCacheKeyFromProxyArray(_CFHTTPRequest *http, CFMutableArrayRef proxyArray, CFURLRef targetURL, CFDictionaryRef connProperties);
static void advanceToNextProxyFromProxyArray(CFMutableArrayRef proxyArray);
#define NUM_SOCKS_PROPS 7
#ifdef __CONSTANT_CFSTRINGS__
#define _kCFHTTPStreamFTPScheme CFSTR("ftp")
#define _kCFHTTPStreamFTPSScheme CFSTR("ftps")
#define _kCFHTTPStreamHTTPScheme CFSTR("http")
#define _kCFHTTPStreamHTTPSScheme CFSTR("https")
#define _kCFHTTPStreamSOCKS4Scheme CFSTR("socks4")
#define _kCFHTTPStreamSOCKS5Scheme CFSTR("socks5")
#define _kCFHTTPStreamUserAgentHeader CFSTR("User-Agent")
#define _kCFHTTPStreamProxyAuthorizationHeader CFSTR("Proxy-Authorization")
#define _kCFHTTPStreamDescribeFormat CFSTR("<HTTP request stream %p>{url = %@, state = %d, flags=%d}")
#define _kCFHTTPStreamContentLengthHeader CFSTR("Content-Length")
#define _kCFHTTPStreamContentLengthFormat CFSTR("%d")
#define _kCFHTTPStreamConnectionHeader CFSTR("Connection")
#define _kCFHTTPStreamProxyConnectionHeader CFSTR("Proxy-Connection")
#define _kCFHTTPStreamConnectionKeepAlive CFSTR("keep-alive")
#define _kCFHTTPStreamConnectionClose CFSTR("close")
#define _kCFHTTPStreamConnectionSeparator CFSTR(",")
#define _kCFHTTPStreamHostHeader CFSTR("Host")
#define _kCFHTTPStreamHostFormat CFSTR("%@:%d")
#define _kCFHTTPStreamLocationHeader CFSTR("Location")
#define _kCFHTTPStreamLocationSeparator CFSTR(", ")
#define _kCFHTTPStreamHEADMethod CFSTR("HEAD")
#define _kCFStreamSocketCreatedCallBack CFSTR("_kCFStreamSocketCreatedCallBack")
#define _kCFHTTPStreamPrivateRunLoopMode CFSTR("_kCFHTTPStreamPrivateRunLoopMode")
#define _kCFNTLMMethod CFSTR("NTLM")
#else
static CONST_STRING_DECL(_kCFHTTPStreamFTPScheme, "ftp")
static CONST_STRING_DECL(_kCFHTTPStreamFTPSScheme, "ftps")
static CONST_STRING_DECL(_kCFHTTPStreamHTTPScheme, "http")
static CONST_STRING_DECL(_kCFHTTPStreamHTTPSScheme, "https")
static CONST_STRING_DECL(_kCFHTTPStreamSOCKS4Scheme, "socks4")
static CONST_STRING_DECL(_kCFHTTPStreamSOCKS5Scheme, "socks5")
static CONST_STRING_DECL(_kCFHTTPStreamUserAgentHeader, "User-Agent")
static CONST_STRING_DECL(_kCFHTTPStreamProxyAuthorizationHeader, "Proxy-Authorization")
static CONST_STRING_DECL(_kCFHTTPStreamDescribeFormat, "<HTTP request stream %p>{url = %@, state = %d, flags=%d}")
static CONST_STRING_DECL(_kCFHTTPStreamContentLengthHeader, "Content-Length")
static CONST_STRING_DECL(_kCFHTTPStreamContentLengthFormat, "%d")
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")
static CONST_STRING_DECL(_kCFHTTPStreamConnectionSeparator, ",")
static CONST_STRING_DECL(_kCFHTTPStreamHostHeader, "Host")
static CONST_STRING_DECL(_kCFHTTPStreamHostFormat, "%@:%d")
static CONST_STRING_DECL(_kCFHTTPStreamLocationHeader, "Location")
static CONST_STRING_DECL(_kCFHTTPStreamLocationSeparator, ", ")
static CONST_STRING_DECL(_kCFHTTPStreamHEADMethod, "HEAD")
static CONST_STRING_DECL(_kCFStreamSocketCreatedCallBack, "_kCFStreamSocketCreatedCallBack")
static CONST_STRING_DECL(_kCFHTTPStreamPrivateRunLoopMode, "_kCFHTTPStreamPrivateRunLoopMode")
static CONST_STRING_DECL(_kCFNTLMMethod, "NTLM")
#endif
static CFSpinLock_t cacheInitLock = 0;
static CFNetConnectionCacheRef httpConnectionCache = NULL;
static void *httpRequestCreate(CFReadStreamRef stream, void *info);
static void httpRequestFinalize(CFReadStreamRef stream, void *info);
static CFStringRef httpRequestDescription(CFReadStreamRef stream, void *info);
static Boolean httpRequestOpen(CFReadStreamRef stream, CFStreamError *error, Boolean *openComplete, void *info);
static Boolean httpRequestOpenCompleted(CFReadStreamRef stream, CFStreamError *error, void *info);
static CFIndex httpRequestRead(CFReadStreamRef stream, UInt8 *buffer, CFIndex bufferLength, CFStreamError *error, Boolean *atEOF, void *info);
static Boolean httpRequestCanRead(CFReadStreamRef stream, void *info);
static void httpRequestClose(CFReadStreamRef stream, void *info);
static CFTypeRef httpRequestCopyProperty(CFReadStreamRef stream, CFStringRef propertyName, void *info);
static Boolean httpRequestSetProperty(CFReadStreamRef stream, CFStringRef propertyName, CFTypeRef propertyValue, void *info);
static void httpRequestSchedule(CFReadStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info);
static void httpRequestUnschedule(CFReadStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info);
static const CFReadStreamCallBacks _CFHTTPQueuedResponseStreamCallBacks = {
1,
httpRequestCreate,
httpRequestFinalize,
httpRequestDescription,
httpRequestOpen,
httpRequestOpenCompleted,
httpRequestRead,
NULL,
httpRequestCanRead,
httpRequestClose,
httpRequestCopyProperty,
httpRequestSetProperty,
NULL, httpRequestSchedule,
httpRequestUnschedule
};
static CFStreamError httpCreateConnectionStreams(CFAllocatorRef allocator, const void *info, CFWriteStreamRef *requestStreams, CFReadStreamRef *responseStreams);
static void httpRequestStateChanged(void *request, int newState, CFStreamError *err, _CFNetConnectionRef connection, const void*);
static void httpTransmitRequest(void *request, _CFNetConnectionRef connection, const void*);
static void httpReceiveResponse(void *request, _CFNetConnectionRef connection, const void*);
static void httpResponseStreamCallBack(void *request, CFReadStreamRef stream, CFStreamEventType type, _CFNetConnectionRef conn, const void*);
static void httpRequestStreamCallBack(void *request, CFWriteStreamRef stream, CFStreamEventType type, _CFNetConnectionRef conn, const void*);
static CFArrayRef httpRunLoopArrayForRequest(void *request, _CFNetConnectionRef conn, const void* info);
static const _CFNetConnectionCallBacks httpConnectionCallBacks = {
0,
connCacheKeyRetain,
connCacheKeyRelease,
httpCreateConnectionStreams,
httpRequestStateChanged,
httpTransmitRequest,
httpReceiveResponse,
httpResponseStreamCallBack,
httpRequestStreamCallBack,
httpRunLoopArrayForRequest
};
static void requestPayloadCallBack(CFReadStreamRef stream, CFStreamEventType type, void *info);
static Boolean resetForRequest(CFHTTPMessageRef newRequest, _CFHTTPRequest *http, CFStreamError *error);
static Boolean checkHeaders(_CFHTTPRequest *http, CFReadStreamRef responseStream, CFStreamError *error, Boolean *connectionStaysPersistent);
static void httpRequestDestroy(CFAllocatorRef alloc, _CFHTTPRequest *req);
static void addAuthenticationInfoToResponse1(_CFHTTPRequest *http);
static CFHTTPAuthenticationRef connectionOrientedAuth(_CFHTTPRequest *req, Boolean forProxy);
extern void _CFSocketStreamCreatePair(CFAllocatorRef alloc, CFStringRef host, UInt32 port, CFSocketNativeHandle s,
const CFSocketSignature* sig, CFReadStreamRef* readStream, CFWriteStreamRef* writeStream);
static void buildDirectDescription(CFHTTPMessageRef request, CFStringRef *host, SInt32 *port, UInt32 *type, CFDictionaryRef *streamProperties) {
CFURLRef targetURL = CFHTTPMessageCopyRequestURL(request);
CFStringRef scheme = CFURLCopyScheme(targetURL);
if (CFEqual(scheme, _kCFHTTPStreamHTTPSScheme)) {
*type = kHTTPS;
} else {
*type = kHTTP;
}
CFRelease(scheme);
*host = CFURLCopyHostName(targetURL);
*port = CFURLGetPortNumber(targetURL);
if (*port == -1) {
*port = (*type == kHTTP) ? 80 : 443;
}
*streamProperties = NULL;
CFRelease(targetURL);
}
static CFDictionaryRef newConnPropsForSOCKSProxy(CFAllocatorRef alloc, CFURLRef proxyURL) {
CFStringRef scheme;
SInt32 port;
CFStringRef user;
CFStringRef keys[5];
CFTypeRef values[5];
CFDictionaryRef socksProxyDict;
CFDictionaryRef newConnProps;
keys[0] = kCFStreamPropertySOCKSProxyHost;
values[0] = CFURLCopyHostName(proxyURL);
keys[1] = kCFStreamPropertySOCKSProxyPort;
port = CFURLGetPortNumber(proxyURL);
values[1] = CFNumberCreate(alloc, kCFNumberSInt32Type, &port);
keys[2] = kCFStreamPropertySOCKSVersion;
scheme = CFURLCopyScheme(proxyURL);
if (CFStringCompare(scheme, _kCFHTTPStreamSOCKS4Scheme, kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
values[2] = kCFStreamSocketSOCKSVersion4;
} else {
values[2] = kCFStreamSocketSOCKSVersion5;
}
CFRelease(scheme);
user = CFURLCopyUserName(proxyURL);
if (user) {
keys[3] = kCFStreamPropertySOCKSUser;
values[3] = user;
keys[4] = kCFStreamPropertySOCKSPassword;
values[4] = CFURLCopyPassword(proxyURL);
}
socksProxyDict = CFDictionaryCreate(alloc, (const void **)keys, values, user ? 5 : 3, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFRelease(values[0]);
CFRelease(values[1]);
if (user) {
CFRelease(user);
CFRelease(values[4]);
}
keys[0] = kCFStreamPropertySOCKSProxy;
values[0] = socksProxyDict;
newConnProps = CFDictionaryCreate(alloc, (const void **)keys, values, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFRelease(socksProxyDict);
return newConnProps;
}
static CFDictionaryRef newConnPropsForHTTPSProxy(CFAllocatorRef alloc, CFHTTPMessageRef req, CFURLRef proxyURL) {
SInt32 port;
CFStringRef keys[3];
CFTypeRef values[3];
CFDictionaryRef headers;
CFDictionaryRef proxyDict;
CFDictionaryRef newConnProps;
keys[0] = _kCFHTTPStreamUserAgentHeader;
values[0] = CFHTTPMessageCopyHeaderFieldValue(req, _kCFHTTPStreamUserAgentHeader);
if (!values[0])
values[0] = CFRetain( _CFNetworkUserAgentString() );
keys[1] = _kCFHTTPStreamProxyAuthorizationHeader;
values[1] = CFHTTPMessageCopyHeaderFieldValue(req, _kCFHTTPStreamProxyAuthorizationHeader);
headers = CFDictionaryCreate(alloc, (const void **)keys, values, values[1] ? 2 : 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFRelease(values[0]);
if (values[1])
CFRelease(values[1]);
keys[0] = kCFStreamPropertyCONNECTProxyHost;
values[0] = CFURLCopyHostName(proxyURL);
keys[1] = kCFStreamPropertyCONNECTProxyPort;
port = CFURLGetPortNumber(proxyURL);
values[1] = CFNumberCreate(alloc, kCFNumberSInt32Type, &port);
keys[2] = kCFStreamPropertyCONNECTAdditionalHeaders;
values[2] = headers;
proxyDict = CFDictionaryCreate(alloc, (const void **)keys, values, 3, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFRelease(values[0]);
CFRelease(values[1]);
CFRelease(values[2]);
keys[0] = kCFStreamPropertyCONNECTProxy;
values[0] = proxyDict;
newConnProps = CFDictionaryCreate(alloc, (const void **)keys, values, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFRelease(proxyDict);
return newConnProps;
}
CF_EXPORT void _CFHTTPGetConnectionInfoForProxyURL(CFURLRef proxyURL, CFHTTPMessageRef request, CFStringRef *host, SInt32 *port, UInt32 *type, CFDictionaryRef *streamProperties) {
if ((CFTypeRef)proxyURL == kCFNull) {
buildDirectDescription(request, host, port, type, streamProperties);
} else {
CFStringRef scheme = CFURLCopyScheme(proxyURL);
if (CFStringCompare(scheme, _kCFHTTPStreamHTTPScheme, kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
*type = kHTTPProxy;
*host = CFURLCopyHostName(proxyURL);
*port = CFURLGetPortNumber(proxyURL);
*streamProperties = NULL;
} else if (CFStringCompare(scheme, _kCFHTTPStreamHTTPSScheme, kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
CFURLRef targetURL = CFHTTPMessageCopyRequestURL(request);
*type = kHTTPSProxy;
*host = CFURLCopyHostName(targetURL);
*port = CFURLGetPortNumber(targetURL);
if (*port == -1) {
CFStringRef scheme = CFURLCopyScheme(targetURL);
if (scheme && (CFStringCompare(scheme, _kCFHTTPStreamHTTPScheme, kCFCompareCaseInsensitive) == kCFCompareEqualTo))
*port = 80;
else
*port = 443;
if (scheme) CFRelease(scheme);
}
*streamProperties = newConnPropsForHTTPSProxy(CFGetAllocator(request), request, proxyURL);
CFRelease(targetURL);
} else if ((CFStringCompare(scheme, _kCFHTTPStreamSOCKS4Scheme, kCFCompareCaseInsensitive) == kCFCompareEqualTo) || (CFStringCompare(scheme, _kCFHTTPStreamSOCKS5Scheme, kCFCompareCaseInsensitive) == kCFCompareEqualTo)) {
CFURLRef targetURL = CFHTTPMessageCopyRequestURL(request);
CFStringRef targetScheme = CFURLCopyScheme(targetURL);
if (CFStringCompare(targetScheme, _kCFHTTPStreamHTTPSScheme, kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
*type = kHTTPS;
} else {
*type = kHTTP;
}
CFRelease(targetScheme);
*host = CFURLCopyHostName(targetURL);
*port = CFURLGetPortNumber(targetURL);
if (*port == -1) {
*port = (*type == kHTTP) ? 80 : 443;
}
*streamProperties = newConnPropsForSOCKSProxy(CFGetAllocator(request), proxyURL);
}
CFRelease(scheme);
}
}
static _CFNetConnectionCacheKey nextConnectionCacheKeyFromProxyArray(_CFHTTPRequest *http, CFMutableArrayRef proxyArray, CFURLRef targetURL, CFDictionaryRef connProperties) {
_CFNetConnectionCacheKey key = NULL;
CFStringRef host;
SInt32 port;
UInt32 type;
CFDictionaryRef additionalProperties, props;
CFMutableDictionaryRef newProps = NULL;
CFURLRef proxyURL = CFArrayGetValueAtIndex(proxyArray, 0);
_CFHTTPGetConnectionInfoForProxyURL(proxyURL, http->currentRequest ? http->currentRequest : http->originalRequest, &host, &port, &type, &additionalProperties);
if (additionalProperties) {
if (!http->connProps) {
props = additionalProperties;
} else {
CFAllocatorRef alloc = CFGetAllocator(http->originalRequest);
CFIndex index, count = CFDictionaryGetCount(additionalProperties);
CFTypeRef *keys = CFAllocatorAllocate(alloc, count * 2 * sizeof(CFTypeRef), 0);
CFTypeRef *values = keys + count;
CFDictionaryGetKeysAndValues(additionalProperties, keys, values);
newProps = CFDictionaryCreateMutableCopy(alloc, CFDictionaryGetCount(http->connProps) + count, http->connProps);
for (index = 0; index < count; index ++) {
CFDictionarySetValue(newProps, keys[index], values[index]);
}
CFAllocatorDeallocate(alloc, keys);
props = newProps;
}
} else if (http->connProps) {
props = http->connProps;
} else {
props = NULL;
}
key = createConnectionCacheKey(host, port, type, props);
if (host) CFRelease(host);
if (additionalProperties) CFRelease(additionalProperties);
if (newProps) CFRelease(newProps);
return key;
}
static void advanceToNextProxyFromProxyArray(CFMutableArrayRef proxyArray) {
if (CFArrayGetCount(proxyArray) > 0) {
CFArrayRemoveValueAtIndex(proxyArray, 0);
}
}
static inline void _CFHTTPRequestSetState(_CFHTTPRequest *req, int newState) {
__CFBitfieldSetValue(req->flags, MAX_STATE_BIT, MIN_STATE_BIT, newState);
}
static inline int _CFHTTPRequestGetState(_CFHTTPRequest *req) {
return __CFBitfieldGetValue(req->flags, MAX_STATE_BIT, MIN_STATE_BIT);
}
#if defined(LOG_REQUESTS)
CF_EXPORT int CFHTTPRequestGetState(_CFHTTPRequest *req);
CF_EXPORT
int CFHTTPRequestGetState(_CFHTTPRequest *req) {
return _CFHTTPRequestGetState(req);
}
#endif
static inline Boolean isPersistent(_CFHTTPRequest *http) {
return __CFBitIsSet(http->flags, IS_PERSISTENT);
}
static inline Boolean haveCheckedHeaders(_CFHTTPRequest *req) {
return __CFBitIsSet(req->flags, HAVE_CHECKED_RESPONSE_HEADERS);
}
static inline Boolean requestHasBeenTransmitted(_CFHTTPRequest *http) {
return __CFBitIsSet(http->flags, HAVE_SENT_REQUEST_PAYLOAD);
}
static int extractSocksProperties(CFDictionaryRef dict, CFTypeRef *outKeys, CFTypeRef *outValues) {
const CFStringRef keys[] = {
kCFStreamPropertySOCKSProxyHost,
kCFStreamPropertySOCKSProxyPort,
kCFStreamPropertySOCKSVersion,
kCFStreamPropertySOCKSUser,
kCFStreamPropertySOCKSPassword,
kCFStreamPropertyProxyExceptionsList,
#if defined(__MACH__)
kSCPropNetProxiesSOCKSEnable
#endif
};
int i, numFound = 0;
for (i = 0; i < (sizeof(keys) / sizeof(keys[0])); i++) {
CFTypeRef value = CFDictionaryGetValue(dict, keys[i]);
if (value) {
outKeys[numFound] = keys[i];
outValues[numFound] = value;
numFound++;
}
}
return numFound;
}
static void *httpRequestCreate(CFReadStreamRef stream, void *info) {
_CFHTTPRequest *newReq, *oldReq = (_CFHTTPRequest *)info;
CFAllocatorRef alloc = CFGetAllocator(stream);
newReq = CFAllocatorAllocate(alloc, sizeof(_CFHTTPRequest), 0);
if (!newReq) return NULL;
newReq->flags = 0;
newReq->connProps = CFDictionaryCreateMutable(alloc, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
_CFHTTPRequestSetState(newReq, kNotQueued);
CFRetain(oldReq->originalRequest);
newReq->originalRequest = oldReq->originalRequest;
newReq->currentRequest = NULL;
newReq->responseHeaders = NULL;
if (oldReq->requestPayload) {
__CFBitSet(newReq->flags, HAS_PAYLOAD);
CFRetain(oldReq->requestPayload);
newReq->requestPayload = oldReq->requestPayload;
} else {
CFDataRef body = CFHTTPMessageCopyBody(newReq->originalRequest);
if (body) {
__CFBitSet(newReq->flags, HAS_PAYLOAD);
__CFBitSet(newReq->flags, PAYLOAD_IS_DATA);
CFRelease(body);
}
newReq->requestPayload = NULL;
}
newReq->peerCertificates = NULL;
newReq->requestFragment = NULL;
newReq->requestBytesWritten = 0;
newReq->responseStream = stream; newReq->proxyDict = NULL;
newReq->proxyList = NULL;
newReq->proxyStream = NULL;
newReq->redirectedURLs = NULL;
newReq->firstRedirection = NULL;
newReq->conn = NULL;
newReq->stateChangeSource = NULL;
#if defined(LOG_REQUESTS)
fprintf(stderr, "Created request 0x%x\n", (int)newReq);
#endif
return newReq;
}
static
_CFHTTPRequest *createZombieDouble1(CFAllocatorRef alloc, _CFHTTPRequest *orig, _CFNetConnectionRef conn) {
_CFHTTPRequest *zombie;
CFArrayRef origRLArray;
#if defined(LOG_REQUESTS)
fprintf(stderr, "substituteZombieDouble(0x%x, 0x%x, 0x%x) -", (int)alloc, (int)orig, (int)(conn));
#endif
zombie = CFAllocatorAllocate(alloc, sizeof(_CFHTTPRequest), 0);
if (!zombie) return NULL;
zombie->flags = orig->flags;
__CFBitSet(zombie->flags, IS_ZOMBIE);
__CFBitClear(zombie->flags, AUTOREDIRECT);
zombie->conn = conn;
CFRetain(conn);
zombie->connProps = CFDictionaryCreateMutable(alloc, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); zombie->firstRedirection = NULL;
zombie->redirectedURLs = NULL;
zombie->proxyDict = NULL;
zombie->proxyList = NULL;
zombie->proxyStream = NULL;
zombie->responseHeaders = NULL;
zombie->requestBytesWritten = orig->requestBytesWritten;
zombie->stateChangeSource = NULL;
zombie->peerCertificates = NULL;
zombie->originalRequest = orig->originalRequest;
CFRetain(zombie->originalRequest);
zombie->currentRequest = orig->currentRequest;
CFRetain(zombie->currentRequest);
zombie->requestFragment = orig->requestFragment;
if (zombie->requestFragment) orig->requestFragment = NULL;
if (orig->requestPayload) {
CFStreamClientContext ctxt = {0, zombie, NULL, NULL, NULL};
zombie->requestPayload = orig->requestPayload;
orig->requestPayload = NULL;
CFReadStreamSetClient(zombie->requestPayload, kCFStreamEventHasBytesAvailable | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered, requestPayloadCallBack, &ctxt);
} else {
zombie->requestPayload = NULL;
}
zombie->responseStream = CFReadStreamCreateWithBytesNoCopy(alloc, (const UInt8*)"dummy zombie stream", strlen("dummy zombie stream"), kCFAllocatorNull);
origRLArray = _CFReadStreamGetRunLoopsAndModes(orig->responseStream);
if (origRLArray) {
CFIndex i, c = CFArrayGetCount(origRLArray);
for (i = 0; i + 1 < c; i += 2) {
CFRunLoopRef rl = (CFRunLoopRef)CFArrayGetValueAtIndex(origRLArray, i);
CFStringRef mode = CFArrayGetValueAtIndex(origRLArray, i + 1);
CFReadStreamScheduleWithRunLoop(zombie->responseStream, rl, mode);
}
}
#if defined(LOG_REQUESTS)
fprintf(stderr, " returned zombie 0x%x\n", (int)zombie);
#endif
return zombie;
}
extern Boolean _CFHTTPAuthenticationConnectionAuthenticated(CFHTTPAuthenticationRef auth, const void* connection);
static Boolean canShutdownConnection(_CFNetConnectionRef conn, _CFHTTPRequest *req, Boolean allowOneEntry) {
int i, bad = 0;
CFHTTPAuthenticationRef auth[2];
Boolean isPersistent = _CFNetConnectionWillEnqueueRequests(conn);
int depth = _CFNetConnectionGetQueueDepth(conn);
Boolean empty = depth == 0 || (allowOneEntry && depth == 1);
Boolean hasAuth = FALSE;
Boolean authComplete = TRUE;
auth[0] = connectionOrientedAuth(req, FALSE);
auth[1] = connectionOrientedAuth(req, TRUE);
for (i = 0; i < (sizeof(auth) / sizeof(auth[0])); i++) {
if (!auth[i])
continue;
hasAuth = TRUE;
if (!isPersistent || !CFHTTPAuthenticationIsValid(auth[i], NULL)) {
bad++;
_CFHTTPAuthenticationDisassociateConnection(auth[i], conn);
}
else
authComplete = authComplete && _CFHTTPAuthenticationConnectionAuthenticated(auth[i], conn);
}
return (!isPersistent ||
(!hasAuth && empty) ||
(hasAuth && bad) ||
(hasAuth && authComplete && empty));
}
static void dequeueFromConnection1(_CFHTTPRequest *req) {
#if defined(LOG_REQUESTS)
fprintf(stderr, " dequeueFromConnection(0x%x)\n", (int)req);
#endif
if (req->conn) {
_CFNetConnectionRef conn = req->conn;
req->conn = NULL;
if (!__CFBitIsSet(req->flags, IS_PERSISTENT)) {
_CFNetConnectionSetAllowsNewRequests(conn, FALSE);
CFRelease(conn);
} else {
if (!_CFNetConnectionDequeue(conn, req)) {
if (canShutdownConnection(conn, req, TRUE)) {
_CFNetConnectionSetAllowsNewRequests(conn, FALSE);
removeFromConnectionCache(httpConnectionCache, conn, (_CFNetConnectionCacheKey)_CFNetConnectionGetInfoPointer(conn));
} else {
_CFHTTPRequest *zombie = createZombieDouble1(CFGetAllocator(conn), req, conn);
if (!zombie) {
req->conn = conn;
__CFBitSet(req->flags, IS_ZOMBIE);
return;
} else {
_CFNetConnectionReplaceRequest(conn, req, zombie);
}
}
} else if (canShutdownConnection(conn, req, FALSE)) {
_CFNetConnectionSetAllowsNewRequests(conn, FALSE);
removeFromConnectionCache(httpConnectionCache, conn, (_CFNetConnectionCacheKey)_CFNetConnectionGetInfoPointer(conn));
}
CFRelease(conn);
}
}
}
static void httpRequestDestroy(CFAllocatorRef alloc, _CFHTTPRequest *req) {
if (req->conn) dequeueFromConnection1(req);
CFRelease(req->originalRequest);
if (req->currentRequest) CFRelease(req->currentRequest);
if (req->responseHeaders) CFRelease(req->responseHeaders);
if (req->requestPayload) {
CFReadStreamClose(req->requestPayload);
CFReadStreamSetClient(req->requestPayload, 0, NULL, NULL);
CFRelease(req->requestPayload);
}
if (__CFBitIsSet(req->flags, IS_ZOMBIE) && req->responseStream) CFRelease(req->responseStream);
if (req->requestFragment) CFRelease(req->requestFragment);
if (req->proxyDict) CFRelease(req->proxyDict);
if (req->proxyList) CFRelease(req->proxyList);
if (req->proxyStream) CFRelease(req->proxyStream);
if (req->redirectedURLs) CFRelease(req->redirectedURLs);
if (req->firstRedirection) CFRelease(req->firstRedirection);
if (req->connProps) CFRelease(req->connProps);
if (req->stateChangeSource) CFRelease(req->stateChangeSource);
if (req->peerCertificates) CFRelease(req->peerCertificates);
CFAllocatorDeallocate(alloc, req);
}
static void httpRequestFinalize(CFReadStreamRef stream, void *info) {
#if defined(LOG_REQUESTS)
fprintf(stderr, " httpRequestFinalize(0x%x, 0x%x)\n", (int)stream, (int)info);
#endif
httpRequestDestroy(CFGetAllocator(stream), (_CFHTTPRequest *)info);
}
static CFStringRef httpRequestDescription(CFReadStreamRef stream, void *info) {
_CFHTTPRequest *req = (_CFHTTPRequest *)info;
CFURLRef url = CFHTTPMessageCopyRequestURL(req->originalRequest);
CFStringRef str = CFStringCreateWithFormat(stream ? CFGetAllocator(stream) : NULL, NULL, _kCFHTTPStreamDescribeFormat, req, url, _CFHTTPRequestGetState(req), req->flags);
CFRelease(url);
return str;
}
static void closeRequestResources1(_CFHTTPRequest *req) {
if (req->requestPayload) {
CFReadStreamClose(req->requestPayload);
CFRelease(req->requestPayload);
req->requestPayload = NULL;
}
if (req->requestFragment) {
CFRelease(req->requestFragment);
req->requestFragment = NULL;
}
}
extern CFStringRef _CFNetworkUserAgentString(void) {
static CFStringRef userAgentString = NULL;
if (!userAgentString) {
CFBundleRef bundle = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.CFNetwork"));
if (bundle) {
CFMutableStringRef mutableString = CFStringCreateMutable(NULL, 0);
CFStringAppendCString(mutableString, "CFNetwork/", kCFStringEncodingASCII);
CFStringAppend(mutableString, CFBundleGetValueForInfoDictionaryKey(bundle, _kCFBundleShortVersionStringKey));
userAgentString = CFStringCreateCopy(NULL, mutableString);
CFRelease(mutableString);
} else {
userAgentString = CFSTR("CFNetwork (unknown version)");
}
}
return userAgentString;
}
extern void cleanUpRequest(CFHTTPMessageRef req, int length, Boolean forPersistentConnection, Boolean forProxy) {
CFURLRef dest;
CFStringRef host;
CFStringRef val;
val = CFHTTPMessageCopyHeaderFieldValue(req, _kCFHTTPStreamUserAgentHeader);
if (val == NULL) {
_CFHTTPMessageSetHeader(req, _kCFHTTPStreamUserAgentHeader, _CFNetworkUserAgentString(), 0);
} else {
CFRelease(val);
}
if (length > -1 && !_CFHTTPMessageIsGetMethod(req)) {
CFStringRef lenStr = CFStringCreateWithFormat(CFGetAllocator(req), NULL, _kCFHTTPStreamContentLengthFormat, length);
CFHTTPMessageSetHeaderFieldValue(req, _kCFHTTPStreamContentLengthHeader, lenStr);
CFRelease(lenStr);
}
if (forPersistentConnection) {
CFHTTPMessageSetHeaderFieldValue(req, _kCFHTTPStreamConnectionHeader, _kCFHTTPStreamConnectionKeepAlive);
if (forProxy) {
CFHTTPMessageSetHeaderFieldValue(req, _kCFHTTPStreamProxyConnectionHeader, _kCFHTTPStreamConnectionKeepAlive);
}
} else {
CFHTTPMessageSetHeaderFieldValue(req, _kCFHTTPStreamConnectionHeader, _kCFHTTPStreamConnectionClose);
if (forProxy) {
CFHTTPMessageSetHeaderFieldValue(req, _kCFHTTPStreamProxyConnectionHeader, _kCFHTTPStreamConnectionClose);
}
}
dest = CFHTTPMessageCopyRequestURL(req);
host = dest ? CFURLCopyHostName(dest) : NULL;
if (host) {
CFStringRef scheme = CFURLCopyScheme(dest);
SInt32 port = CFURLGetPortNumber(dest);
if (port == -1) {
CFHTTPMessageSetHeaderFieldValue(req, _kCFHTTPStreamHostHeader, host);
} else {
CFStringRef hostStr = CFStringCreateWithFormat(CFGetAllocator(req), NULL, _kCFHTTPStreamHostFormat, host, port);
if (hostStr) {
CFHTTPMessageSetHeaderFieldValue(req, _kCFHTTPStreamHostHeader, hostStr);
CFRelease(hostStr);
}
}
CFRelease(host);
if (scheme) CFRelease(scheme);
}
if (dest) CFRelease(dest);
}
static inline Boolean isConnectionToProxy(_CFNetConnectionRef conn) {
_CFNetConnectionCacheKey key = (_CFNetConnectionCacheKey)_CFNetConnectionGetInfoPointer(conn);
CFStringRef host;
SInt32 port;
UInt32 type;
CFDictionaryRef props;
getValuesFromKey(key, &host, &port, &type, &props);
return (type == kHTTPProxy) || (type == kHTTPSProxy);
}
static void prepareTransmission1(_CFHTTPRequest *req, CFWriteStreamRef requestStream, _CFNetConnectionRef conn) {
Boolean reqIsPersistent = isPersistent(req);
CFStreamClientContext ctxt = {0, req, NULL, NULL, NULL};
CFDataRef payload = NULL;
Boolean forProxy = isConnectionToProxy(conn);
if (__CFBitIsSet(req->flags, PAYLOAD_IS_DATA) && (payload = CFHTTPMessageCopyBody(req->originalRequest)) != NULL) {
CFIndex length = CFDataGetLength(payload);
if (req->requestPayload) {
CFReadStreamSetClient(req->requestPayload, kCFStreamEventNone, NULL, NULL);
CFReadStreamClose(req->requestPayload);
CFRelease(req->requestPayload);
}
if (length) {
req->requestPayload = CFReadStreamCreateWithBytesNoCopy(CFGetAllocator(payload), CFDataGetBytePtr(payload), length, kCFAllocatorNull);
}
else
req->requestPayload = NULL;
CFRelease(payload); cleanUpRequest(req->currentRequest, length, reqIsPersistent, forProxy);
} else if (!req->requestPayload) {
cleanUpRequest(req->currentRequest, 0, reqIsPersistent, forProxy);
} else {
cleanUpRequest(req->currentRequest, -1, reqIsPersistent, forProxy);
}
if (req->requestPayload) {
CFArrayRef rlArray;
CFReadStreamSetClient(req->requestPayload, kCFStreamEventHasBytesAvailable | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered, requestPayloadCallBack, &ctxt);
rlArray = _CFReadStreamGetRunLoopsAndModes(req->responseStream);
if (rlArray) {
int i, c = CFArrayGetCount(rlArray);
for (i = 0; i + 1 < c; i += 2) {
CFRunLoopRef rl = (CFRunLoopRef)CFArrayGetValueAtIndex(rlArray, i);
CFStringRef mode = CFArrayGetValueAtIndex(rlArray, i + 1);
if (req->requestPayload) {
CFReadStreamScheduleWithRunLoop(req->requestPayload, rl, mode);
}
}
}
CFReadStreamOpen(req->requestPayload);
}
CFWriteStreamSetProperty(requestStream, _kCFStreamPropertyHTTPNewHeader, req->currentRequest);
if (!__CFBitIsSet(req->flags, OPEN_SIGNALLED)) {
CFReadStreamSignalEvent(req->responseStream, kCFStreamEventOpenCompleted, NULL);
__CFBitSet(req->flags, OPEN_SIGNALLED);
}
if (CFWriteStreamCanAcceptBytes(requestStream)) {
_CFWriteStreamSignalEventDelayed(requestStream, kCFStreamEventCanAcceptBytes, NULL);
}
}
static void concludeTransmission1(_CFHTTPRequest *req, CFWriteStreamRef requestStream) {
closeRequestResources1(req);
_CFHTTPWriteStreamWriteMark(requestStream);
}
static void prepareReception1(_CFHTTPRequest *req, CFReadStreamRef responseStream) {
CFStringRef cmd = NULL;
if (__CFBitIsSet(req->flags, HAVE_READ_MARK)) return;
__CFBitSet(req->flags, HAVE_READ_MARK);
_CFHTTPReadStreamReadMark(responseStream);
if (req->originalRequest && (cmd = CFHTTPMessageCopyRequestMethod(req->originalRequest)) && CFEqual(cmd, _kCFHTTPStreamHEADMethod)) {
CFReadStreamSetProperty(responseStream, _kCFStreamPropertyHTTPZeroLengthResponseExpected, kCFBooleanTrue);
}
if (cmd) CFRelease(cmd);
if (CFReadStreamHasBytesAvailable(responseStream)) {
_CFReadStreamSignalEventDelayed(responseStream, kCFStreamEventHasBytesAvailable, NULL);
} else if (_CFHTTPReadStreamIsAtMark(responseStream)) {
_CFReadStreamSignalEventDelayed(responseStream, kCFStreamEventMarkEncountered, NULL);
}
}
static void concludeReception1(_CFHTTPRequest *req, CFReadStreamRef responseStream, Boolean *haveBeenDealloced) {
Boolean readFromThisStream = TRUE;
CFStreamError err = {0, 0};
if (__CFBitIsSet(req->flags, IS_ZOMBIE)) {
CFAllocatorRef alloc = CFGetAllocator(req->conn);
dequeueFromConnection1(req);
httpRequestDestroy(alloc, req);
*haveBeenDealloced = TRUE;
} else {
if (!haveCheckedHeaders(req)) {
Boolean persistentOK;
readFromThisStream = checkHeaders(req, responseStream, &err, &persistentOK);
if (isPersistent(req)) {
if (!persistentOK) {
_CFNetConnectionLost(req->conn);
#if !defined(NO_PIPELINING)
} else {
_CFNetConnectionSetShouldPipeline(req->conn, TRUE);
#endif
}
}
}
if (err.error != 0) {
_CFReadStreamSignalEventDelayed(req->responseStream, kCFStreamEventErrorOccurred, &err);
} else if (readFromThisStream) {
_CFReadStreamSignalEventDelayed(req->responseStream, kCFStreamEventEndEncountered, NULL);
}
}
}
static Boolean shouldReattemptRequest(_CFHTTPRequest *req, CFStreamError *err, int oldState, _CFNetConnectionRef conn, Boolean *advanceToNextProxy) {
*advanceToNextProxy = FALSE;
if (__CFBitIsSet(req->flags, IS_ZOMBIE)) {
return FALSE;
} else if (!isPersistent(req) || __CFBitIsSet(req->flags, DO_NOT_REATTEMPT)) {
if (oldState == kTransmittingRequest && req->proxyList && CFArrayGetCount(req->proxyList) > 1 && (!__CFBitIsSet(req->flags, HAS_PAYLOAD) || __CFBitIsSet(req->flags, PAYLOAD_IS_DATA))) {
*advanceToNextProxy = TRUE;
return TRUE;
} else {
return FALSE;
}
} else if (oldState == kNotQueued || oldState == kQueued) {
return TRUE;
} else if (__CFBitIsSet(req->flags, HAS_PAYLOAD) && !__CFBitIsSet(req->flags, PAYLOAD_IS_DATA)) {
return FALSE;
} else if (err->domain == kCFStreamErrorDomainHTTP && err->error == kCFStreamErrorHTTPConnectionLost) {
return TRUE;
} else if (oldState == kTransmittingRequest) {
if (req->proxyList && CFArrayGetCount(req->proxyList) > 1) {
*advanceToNextProxy = TRUE;
return TRUE;
} else {
return FALSE;
}
} else {
return TRUE;
}
}
static Boolean performReattempt(_CFHTTPRequest *req, Boolean advanceToNextProxy) {
CFStreamError dummy; dequeueFromConnection1(req);
closeRequestResources1(req);
if (advanceToNextProxy) {
advanceToNextProxyFromProxyArray(req->proxyList);
}
return resetForRequest(req->currentRequest, req, &dummy);
}
static void haveBeenOrphaned1(_CFHTTPRequest *req, int oldState, CFStreamError *err, _CFNetConnectionRef conn, Boolean *haveBeenDealloced) {
Boolean shouldReattempt;
Boolean advanceToNextProxy;
*haveBeenDealloced = FALSE;
shouldReattempt = shouldReattemptRequest(req, err, oldState, conn, &advanceToNextProxy);
if (shouldReattempt) {
if (!performReattempt(req, advanceToNextProxy)) {
if (err->domain == kCFStreamErrorDomainHTTP && err->error == kCFStreamErrorHTTPConnectionLost) {
err->domain = _kCFStreamErrorDomainNativeSockets;
err->error = ECONNRESET;
}
_CFReadStreamSignalEventDelayed(req->responseStream, kCFStreamEventErrorOccurred, err);
}
} else {
if (req->requestPayload) {
CFReadStreamClose(req->requestPayload);
CFRelease(req->requestPayload);
req->requestPayload = NULL;
}
if (req->requestFragment) {
CFRelease(req->requestFragment);
req->requestFragment = NULL;
}
if (!__CFBitIsSet(req->flags, IS_ZOMBIE)) {
if (err->domain == kCFStreamErrorDomainHTTP && err->error == _kCFStreamErrorHTTPSProxyFailure) {
req->responseHeaders = (CFHTTPMessageRef)CFWriteStreamCopyProperty(_CFNetConnectionGetRequestStream(conn), kCFStreamPropertyCONNECTResponse);
addAuthenticationInfoToResponse1(req);
_CFReadStreamSignalEventDelayed(req->responseStream, kCFStreamEventEndEncountered, NULL);
} else {
if (err->domain == kCFStreamErrorDomainHTTP && err->error == kCFStreamErrorHTTPConnectionLost) {
err->domain = _kCFStreamErrorDomainNativeSockets;
err->error = ECONNRESET;
}
_CFReadStreamSignalEventDelayed(req->responseStream, kCFStreamEventErrorOccurred, err);
}
dequeueFromConnection1(req);
} else {
dequeueFromConnection1(req);
httpRequestDestroy(CFGetAllocator(conn), req);
*haveBeenDealloced = TRUE;
}
}
}
static void httpRequestStateChanged(void *request, int newState, CFStreamError *err, _CFNetConnectionRef conn, const void* key) {
_CFHTTPRequest *req = (_CFHTTPRequest *)request;
int oldState = _CFHTTPRequestGetState(req);
Boolean haveBeenDealloced = FALSE;
#if defined(LOG_REQUESTS)
fprintf(stderr, "httpRequestStateChanged(req = 0x%x, newState = %d, conn = 0x%x)\n", (int)req, newState, (int)conn);
#endif
_CFHTTPRequestSetState(req, newState);
switch (newState) {
case kQueued:
break;
case kTransmittingRequest:
prepareTransmission1(req, _CFNetConnectionGetRequestStream(conn), conn);
break;
case kWaitingForResponse:
concludeTransmission1(req, _CFNetConnectionGetRequestStream(conn));
break;
case kReceivingResponse:
prepareReception1(req, _CFNetConnectionGetResponseStream(conn));
break;
case kFinished:
concludeReception1(req, _CFNetConnectionGetResponseStream(conn), &haveBeenDealloced);
break;
case kOrphaned: {
haveBeenOrphaned1(req, oldState, err, conn, &haveBeenDealloced);
break;
}
default:
CFLog(0, CFSTR("Encountered unexpected state %d for request 0x%x"), newState, req);
}
if (!haveBeenDealloced && req->stateChangeSource) {
CFRunLoopSourceSignal(req->stateChangeSource);
}
}
static Boolean transmitRequest1(_CFHTTPRequest *http, CFWriteStreamRef destStream, CFStreamError *error, Boolean blockOnce) {
Boolean done = FALSE;
UInt8 buf[BUF_SIZE];
const UInt8 *bytes;
error->error = 0;
if (__CFBitIsSet(http->flags, HAVE_SENT_REQUEST_PAYLOAD)) return TRUE;
if (CFWriteStreamCopyProperty(destStream, _kCFStreamPropertyHTTPSProxyHoldYourFire))
return TRUE;
if (http->requestPayload == NULL) {
if (CFWriteStreamCanAcceptBytes(destStream)) {
done = TRUE;
} else if (blockOnce) {
CFStreamStatus status = CFWriteStreamGetStatus(destStream);
while ((status == kCFStreamStatusOpening || status == kCFStreamStatusOpen) && !CFWriteStreamCanAcceptBytes(destStream)) {
usleep(5); status = CFWriteStreamGetStatus(destStream);
}
done = TRUE;
} else if (CFWriteStreamGetStatus(destStream) == kCFStreamStatusError) {
*error = CFWriteStreamGetError(destStream);
done = TRUE;
} else {
return FALSE;
}
}
if (!done) {
CFStreamStatus status = CFReadStreamGetStatus(http->requestPayload);
if (status == kCFStreamStatusError) {
*error = CFReadStreamGetError(http->requestPayload);
done = TRUE;
} else {
status = CFWriteStreamGetStatus(destStream);
if (status == kCFStreamStatusError) {
*error = CFWriteStreamGetError(destStream);
done = TRUE;
} else if (status == kCFStreamStatusAtEnd) {
done = TRUE;
error->domain = kCFStreamErrorDomainHTTP;
error->error = kCFStreamErrorHTTPParseFailure;
}
}
}
while (!done && (http->requestFragment || blockOnce || (CFReadStreamHasBytesAvailable(http->requestPayload) && CFWriteStreamCanAcceptBytes(destStream)))) {
CFIndex bytesRead, bytesWritten;
if (http->requestFragment) {
bytesRead = CFDataGetLength(http->requestFragment);
bytes = CFDataGetBytePtr(http->requestFragment);
} else {
bytesRead = CFReadStreamRead(http->requestPayload, buf, BUF_SIZE);
bytes = buf;
if (bytesRead < 0) {
*error = CFReadStreamGetError(http->requestPayload);
done = TRUE;
} else if (bytesRead == 0) {
done = TRUE;
}
}
while (!done && bytesRead > 0 && (blockOnce || CFWriteStreamCanAcceptBytes(destStream))) {
bytesWritten = CFWriteStreamWrite(destStream, bytes, bytesRead);
if (bytesWritten < 0) {
*error = CFWriteStreamGetError(destStream);
done = TRUE;
} else if (bytesWritten == 0) {
done = TRUE;
error->domain = kCFStreamErrorDomainHTTP;
error->error = kCFStreamErrorHTTPParseFailure;
} else {
bytesRead -= bytesWritten;
bytes += bytesWritten;
http->requestBytesWritten += bytesWritten;
}
}
if (http->requestFragment) {
if (done || bytesRead <= 0) {
CFRelease(http->requestFragment);
http->requestFragment = NULL;
} else {
CFDataRef newData = CFDataCreate(CFGetAllocator(http->originalRequest), bytes, bytesRead);
CFRelease(http->requestFragment);
http->requestFragment = newData;
}
} else if (bytesRead > 0) {
http->requestFragment = CFDataCreate(CFGetAllocator(http->originalRequest), bytes, bytesRead);
}
blockOnce = FALSE;
}
if (!done && !http->requestFragment && CFReadStreamGetStatus(http->requestPayload) == kCFStreamStatusAtEnd) {
done = TRUE;
}
if (done) {
closeRequestResources1(http);
__CFBitSet(http->flags, HAVE_SENT_REQUEST_PAYLOAD);
}
return done;
}
static
void httpTransmitRequest(void *request, _CFNetConnectionRef connection, const void* key) {
_CFHTTPRequest *req = (_CFHTTPRequest *)request;
CFWriteStreamRef requestStream = _CFNetConnectionGetRequestStream(connection);
CFStreamError error;
Boolean requestTransmitted;
#if defined(LOG_REQUESTS)
fprintf(stderr, "httpTransmitRequest(req = 0x%x, requestStream = 0x%x, conn = 0x%x)\n", (int)req, (int)requestStream, (int)connection);
#endif
requestTransmitted = transmitRequest1(req, requestStream, &error, FALSE);
if (error.error != 0) {
if (error.domain == kCFStreamErrorDomainHTTP && error.error == _kCFStreamErrorHTTPSProxyFailure) {
req->responseHeaders = (CFHTTPMessageRef)CFWriteStreamCopyProperty(requestStream, kCFStreamPropertyCONNECTResponse);
addAuthenticationInfoToResponse1(req);
__CFBitSet(req->flags, HAVE_CHECKED_RESPONSE_HEADERS);
_CFNetConnectionRequestIsComplete(connection, req);
_CFNetConnectionLost(connection); } else {
if (error.domain != kCFStreamErrorDomainHTTP || error.error != kCFStreamErrorHTTPConnectionLost) {
__CFBitSet(req->flags, DO_NOT_REATTEMPT);
}
_CFNetConnectionErrorOccurred(connection, &error);
}
} else if (requestTransmitted) {
_CFNetConnectionRequestIsComplete(connection, req);
}
}
static void httpReceiveResponse(void *request, _CFNetConnectionRef conn, const void* key) {
_CFHTTPRequest *req = (_CFHTTPRequest *)request;
CFReadStreamRef responseStream = _CFNetConnectionGetResponseStream(conn);
Boolean useThisStream;
int state;
#if defined(LOG_REQUESTS)
fprintf(stderr, "httpRequestReceiveResponse(req = 0x%x, responseStream = 0x%x, conn = 0x%x)\n", (int)req, (int)responseStream, (int)(conn));
#endif
if (__CFBitIsSet(req->flags, IN_READ_CALLBACK)) return; if (!responseStream) return;
state = CFReadStreamGetStatus(responseStream);
if (state != kCFStreamStatusAtEnd && state != kCFStreamStatusError && !CFReadStreamHasBytesAvailable(responseStream) && !_CFHTTPReadStreamIsAtMark(responseStream)) return;
if (!haveCheckedHeaders(req)) {
CFStreamError err;
Boolean persistentOK;
useThisStream = checkHeaders(req, responseStream, &err, &persistentOK);
if (err.error != 0) {
if (err.domain != kCFStreamErrorDomainHTTP || err.error != kCFStreamErrorHTTPConnectionLost) {
__CFBitSet(req->flags, DO_NOT_REATTEMPT);
}
_CFNetConnectionErrorOccurred(conn, &err);
} else {
if (!persistentOK) {
_CFNetConnectionLost(conn);
}
if (__CFBitIsSet(req->flags, FORCE_EOF)) {
_CFNetConnectionResponseIsComplete(conn, req);
useThisStream = FALSE;
}
}
} else {
useThisStream = TRUE;
}
if (useThisStream) {
if (CFReadStreamHasBytesAvailable(responseStream)) {
if (__CFBitIsSet(req->flags, IS_ZOMBIE)) {
UInt8 buf[BUF_SIZE];
CFIndex bytesRead;
while (CFReadStreamHasBytesAvailable(responseStream)) {
bytesRead = CFReadStreamRead(responseStream, buf, BUF_SIZE);
if (bytesRead < 0) {
CFStreamError err = CFReadStreamGetError(responseStream);
__CFBitSet(req->flags, DO_NOT_REATTEMPT);
_CFNetConnectionErrorOccurred(conn, &err);
break;
} else if (bytesRead == 0) {
break;
}
}
} else {
_CFReadStreamSignalEventDelayed(req->responseStream, kCFStreamEventHasBytesAvailable, NULL);
}
} else if (_CFHTTPReadStreamIsAtMark(responseStream)) {
_CFNetConnectionResponseIsComplete(conn, req);
} else if (CFReadStreamGetStatus(responseStream) == kCFStreamStatusAtEnd) {
_CFNetConnectionLost(conn);
_CFNetConnectionResponseIsComplete(conn, req);
} else if (CFReadStreamGetStatus(responseStream) == kCFStreamStatusError) {
CFStreamError err = CFReadStreamGetError(responseStream);
if (err.domain != kCFStreamErrorDomainHTTP || err.error != kCFStreamErrorHTTPConnectionLost) {
__CFBitSet(req->flags, DO_NOT_REATTEMPT);
}
_CFNetConnectionErrorOccurred(conn, &err);
}
}
}
static CFHTTPAuthenticationRef connectionOrientedAuth(_CFHTTPRequest *http, Boolean forProxy) {
CFHTTPAuthenticationRef auth = _CFHTTPMessageGetAuthentication(http->currentRequest, forProxy);
CFHTTPAuthenticationRef connAuth = NULL;
if (auth) {
CFStringRef method = CFHTTPAuthenticationCopyMethod(auth);
if (method) {
if (method == kCFHTTPAuthenticationSchemeNegotiate || method == kCFHTTPAuthenticationSchemeNTLM) {
connAuth = auth;
}
CFRelease(method);
}
}
return connAuth;
}
static void setConnectionFromProxyStream(_CFHTTPRequest *http, CFStreamError *err) {
Boolean isComplete;
err->domain = 0;
http->proxyList = _CFNetworkCopyProxyFromProxyStream(http->proxyStream, &isComplete);
if (!isComplete) {
return;
}
CFRelease(http->proxyStream);
http->proxyStream = NULL;
if (!http->proxyList || CFArrayGetCount(http->proxyList) == 0) {
err->domain = kCFStreamErrorDomainHTTP;
err->error = kCFStreamErrorHTTPParseFailure;
} else {
CFHTTPAuthenticationRef auth = connectionOrientedAuth(http, FALSE);
CFHTTPAuthenticationRef proxyAuth = connectionOrientedAuth(http, TRUE);
if (auth || proxyAuth) {
__CFBitSet(http->flags, IS_PERSISTENT);
}
CFHTTPMessageRef request = http->currentRequest ? http->currentRequest : http->originalRequest;
CFURLRef targetURL = CFHTTPMessageCopyRequestURL(request);
_CFNetConnectionCacheKey key = nextConnectionCacheKeyFromProxyArray(http, http->proxyList, targetURL, http->connProps);
CFRelease(targetURL);
__CFSpinLock(&cacheInitLock);
if (httpConnectionCache == NULL) {
httpConnectionCache = createConnectionCache();
}
__CFSpinUnlock(&cacheInitLock);
http->conn = findOrCreateNetConnection(httpConnectionCache, CFGetAllocator(http->responseStream), &httpConnectionCallBacks, key, key, isPersistent(http), http->connProps);
releaseConnectionCacheKey(key);
if ((auth || proxyAuth) && http->conn) {
err->error = 0;
err->domain = 0;
if (auth)
*err = _CFHTTPAuthenticationApplyHeaderToRequest(auth, request, http->conn);
if (!err->error && proxyAuth)
*err = _CFHTTPAuthenticationApplyHeaderToRequest(proxyAuth, request, http->conn);
if (err->error) {
CFRelease(http->conn);
http->conn = NULL;
return;
}
}
_CFNetConnectionEnqueue(http->conn, http);
if (!isPersistent(http)) {
_CFNetConnectionSetAllowsNewRequests(http->conn, FALSE);
}
}
}
static void proxyInfoAvailable(CFReadStreamRef proxyStream, void *clientInfo) {
_CFHTTPRequest *http = (_CFHTTPRequest *)clientInfo;
CFStreamError err;
if (__CFBitIsSet(http->flags, WAITING_FOR_PROXY_STREAM)) {
__CFBitClear(http->flags, WAITING_FOR_PROXY_STREAM);
} else {
setConnectionFromProxyStream(http, &err);
if (err.domain != 0) {
CFReadStreamSignalEvent(http->responseStream, kCFStreamEventErrorOccurred, &err);
}
}
}
static _CFNetConnectionRef getConnectionForRequest(_CFHTTPRequest *req, Boolean *created, CFStreamError *error) {
_CFNetConnectionRef conn = NULL;
CFHTTPAuthenticationRef auth;
CFHTTPAuthenticationRef proxyAuth;
error->domain = 0;
error->error = 0;
#if (DEBUG)
if (__CFBitIsSet(req->flags, IS_ZOMBIE)) {
CFLog(0, CFSTR("Asked to enqueue a zombie request <0x%x>"), req);
error->domain = kCFStreamErrorDomainHTTP;
error->error = -1000; return NULL;
}
#endif
auth = connectionOrientedAuth(req, FALSE);
proxyAuth = connectionOrientedAuth(req, TRUE);
if (auth || proxyAuth) {
__CFBitSet(req->flags, IS_PERSISTENT);
}
CFURLRef targetURL = CFHTTPMessageCopyRequestURL(req->currentRequest ? req->currentRequest : req->originalRequest);
CFStringRef scheme = targetURL ? CFURLCopyScheme(targetURL) : NULL;
if (!targetURL) {
error->domain = kCFStreamErrorDomainHTTP;
error->error = kCFStreamErrorHTTPBadURL;
conn = NULL;
} else if (!req->proxyList && (scheme == NULL)) {
error->domain = kCFStreamErrorDomainHTTP;
error->error = kCFStreamErrorHTTPBadURL;
conn = NULL;
} else {
CFReadStreamRef proxyStream = NULL;
if (!req->proxyList) {
CFStringRef proxyScheme = NULL;
if (CFStringCompare(scheme, _kCFHTTPStreamFTPScheme, kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
proxyScheme = _kCFHTTPStreamHTTPScheme;
} else if (CFStringCompare(scheme, _kCFHTTPStreamFTPSScheme, kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
proxyScheme = _kCFHTTPStreamHTTPSScheme;
}
req->proxyList = _CFNetworkFindProxyForURLAsync(proxyScheme, targetURL, NULL, req->proxyDict, proxyInfoAvailable, req, &proxyStream);
}
if (!req->proxyList) {
CFArrayRef rlArray = _CFReadStreamGetRunLoopsAndModes(req->responseStream);
CFIndex count;
req->proxyStream = proxyStream;
conn = NULL;
if (rlArray && (count = CFArrayGetCount(rlArray)) != 0) {
CFIndex index;
for (index = 0; index+1 < count; index += 2) {
CFRunLoopRef rl = (CFRunLoopRef)CFArrayGetValueAtIndex(rlArray, index);
CFStringRef mode = CFArrayGetValueAtIndex(rlArray, index + 1);
CFReadStreamScheduleWithRunLoop(proxyStream, rl, mode);
}
}
} else if (CFArrayGetCount(req->proxyList) == 0) {
error->domain = kCFStreamErrorDomainHTTP;
error->error = kCFStreamErrorHTTPParseFailure;
conn = NULL;
} else {
_CFNetConnectionCacheKey key = nextConnectionCacheKeyFromProxyArray(req, req->proxyList, targetURL, req->connProps);
__CFSpinLock(&cacheInitLock);
if (httpConnectionCache == NULL) {
httpConnectionCache = createConnectionCache();
}
__CFSpinUnlock(&cacheInitLock);
conn = findOrCreateNetConnection(httpConnectionCache, CFGetAllocator(req->responseStream), &httpConnectionCallBacks, key, key, isPersistent(req), req->connProps);
releaseConnectionCacheKey(key);
}
}
if ((auth || proxyAuth) && conn) {
if (auth)
*error = _CFHTTPAuthenticationApplyHeaderToRequest(auth, req->currentRequest, conn);
if (!error->error && proxyAuth) {
*error = _CFHTTPAuthenticationApplyHeaderToRequest(proxyAuth, req->currentRequest, conn);
if (!error->error) {
CFStringRef method = CFHTTPAuthenticationCopyMethod(proxyAuth);
if (method && (CFStringCompare(method, _kCFNTLMMethod, kCFCompareCaseInsensitive) == kCFCompareEqualTo)) {
if (scheme && (CFStringCompare(scheme, _kCFHTTPStreamHTTPSScheme, kCFCompareCaseInsensitive) == kCFCompareEqualTo)) {
CFAllocatorRef alloc = CFGetAllocator(req->responseStream);
CFMutableDictionaryRef new_value = NULL;
CFStringRef header;
CFDictionaryRef property = CFWriteStreamCopyProperty(_CFNetConnectionGetRequestStream(conn), kCFStreamPropertyCONNECTProxy);
if (!property)
property = CFDictionaryGetValue(req->connProps, kCFStreamPropertyCONNECTProxy);
new_value = property ? CFDictionaryCreateMutableCopy(alloc, 0, property) : CFDictionaryCreateMutable(alloc, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if (property) CFRelease(property);
header = CFHTTPMessageCopyHeaderFieldValue(req->currentRequest, _kCFHTTPStreamProxyAuthorizationHeader);
if (header) {
CFMutableDictionaryRef headers = NULL;
property = CFDictionaryGetValue(new_value, kCFStreamPropertyCONNECTAdditionalHeaders);
if (property)
headers = CFDictionaryCreateMutableCopy(alloc, 0, property);
else
headers = CFDictionaryCreateMutable(alloc, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFDictionarySetValue(headers, _kCFHTTPStreamProxyAuthorizationHeader, header);
CFRelease(header);
CFDictionarySetValue(headers, _kCFHTTPStreamProxyConnectionHeader, _kCFHTTPStreamConnectionKeepAlive);
CFDictionarySetValue(new_value, kCFStreamPropertyCONNECTAdditionalHeaders, headers);
CFRelease(headers);
}
CFWriteStreamSetProperty(_CFNetConnectionGetRequestStream(conn), kCFStreamPropertyCONNECTProxy, new_value);
CFRelease(new_value);
}
}
if (method) CFRelease(method);
}
}
if (error->error) {
CFRelease(conn);
conn = NULL;
}
}
if (scheme) CFRelease(scheme);
if (targetURL) CFRelease(targetURL);
return conn;
}
static void _CFStreamSocketCreatedCallBack(int fd, void* ctxt) {
int yes = 1;
(void)ctxt;
setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (void*)&yes, sizeof(yes));
}
static CFStreamError httpCreateConnectionStreams(CFAllocatorRef alloc, const void *info, CFWriteStreamRef *requestStream, CFReadStreamRef *responseStream) {
const _CFNetConnectionCacheKey key = (_CFNetConnectionCacheKey)info;
CFReadStreamRef rStream;
CFWriteStreamRef wStream;
CFStreamError err = {0, 0};
CFIndex count;
CFArrayRef array;
CFStringRef host;
SInt32 port;
UInt32 connType;
CFDictionaryRef properties;
getValuesFromKey(key, &host, &port, &connType, &properties);
if (properties && (array = CFDictionaryGetValue(properties, _kCFStreamPropertyHTTPConnectionStreams)) != NULL) {
rStream = (CFReadStreamRef)CFArrayGetValueAtIndex(array, 0);
wStream = (CFWriteStreamRef)CFArrayGetValueAtIndex(array, 1);
CFRetain(rStream);
CFRetain(wStream);
} else {
CFArrayRef callback;
CFArrayCallBacks cb = {0, NULL, NULL, NULL, NULL};
const void* values[2] = {_CFStreamSocketCreatedCallBack, NULL};
_CFSocketStreamCreatePair(alloc, host, port, 0, NULL, &rStream, &wStream);
if (!rStream) {
err.domain = kCFStreamErrorDomainPOSIX;
err.error = ENOMEM;
return err;
}
callback = CFArrayCreate(alloc, values, sizeof(values) / sizeof(values[0]), &cb);
if (callback) {
CFWriteStreamSetProperty(wStream, _kCFStreamSocketCreatedCallBack, callback);
CFRelease(callback);
}
if (connType == kHTTPS) {
CFReadStreamSetProperty(rStream, kCFStreamPropertySocketSecurityLevel, kCFStreamSocketSecurityLevelNegotiatedSSL);
}
}
*requestStream = CFWriteStreamCreateHTTPStream(alloc, NULL, (connType == kHTTPProxy || connType == kHTTPSProxy), wStream);
CFWriteStreamSetProperty(*requestStream, _kCFStreamPropertyHTTPPersistent, kCFBooleanTrue);
CFRelease(wStream);
*responseStream = CFReadStreamCreateHTTPStream(alloc, rStream, TRUE);
CFReadStreamSetProperty(*responseStream, _kCFStreamPropertyHTTPPersistent, kCFBooleanTrue);
CFRelease(rStream);
if (properties && (count = CFDictionaryGetCount(properties)) > 0) {
CFStringRef *keys = CFAllocatorAllocate(alloc, sizeof(CFStringRef)*count*2, 0);
CFTypeRef *values = (CFTypeRef *)(keys + count);
CFIndex index;
CFDictionaryGetKeysAndValues(properties, (const void **)keys, (const void **)values);
for (index = 0; index < count; index ++) {
CFReadStreamSetProperty(*responseStream, keys[index], values[index]);
CFWriteStreamSetProperty(*requestStream, keys[index], values[index]);
}
CFAllocatorDeallocate(alloc, keys);
}
return err;
}
static Boolean resetForRequest(CFHTTPMessageRef newRequest, _CFHTTPRequest *http, CFStreamError *error) {
__CFBitClear(http->flags, HAVE_SENT_REQUEST_HEADERS);
__CFBitClear(http->flags, HAVE_SENT_REQUEST_PAYLOAD);
__CFBitClear(http->flags, HAVE_CHECKED_RESPONSE_HEADERS);
__CFBitClear(http->flags, FORCE_EOF);
__CFBitClear(http->flags, HAVE_READ_MARK);
if (newRequest != http->currentRequest) {
if (http->currentRequest) CFRelease(http->currentRequest);
CFRetain(newRequest);
http->currentRequest = newRequest;
http->requestBytesWritten = 0;
}
if (http->conn && !_CFNetConnectionWillEnqueueRequests(http->conn)) {
dequeueFromConnection1(http);
}
if (!http->conn) {
Boolean dummy;
http->conn = getConnectionForRequest(http, &dummy, error);
}
if (error->domain != 0) {
return FALSE;
} else if (!http->conn) {
return TRUE;
} else {
_CFNetConnectionEnqueue(http->conn, http);
if (!isPersistent(http)) {
_CFNetConnectionSetAllowsNewRequests(http->conn, FALSE);
}
return TRUE;
}
}
static Boolean httpRequestOpen(CFReadStreamRef stream, CFStreamError *error, Boolean *openComplete, void *info) {
_CFHTTPRequest *http = (_CFHTTPRequest *)info;
CFHTTPMessageRef newRequest = CFHTTPMessageCreateCopy(CFGetAllocator(stream), http->originalRequest);
Boolean result;
#if defined(LOG_REQUESTS)
fprintf(stderr, "httpRequestOpen(req = 0x%x)\n", (int)http);
#endif
if (!resetForRequest(newRequest, http, error)) {
*openComplete = TRUE;
result = FALSE;
} else {
*openComplete = http->proxyStream ? FALSE : (_CFHTTPRequestGetState(http) > kQueued);
result = TRUE;
}
CFRelease(newRequest);
return result;
}
static Boolean httpRequestOpenCompleted(CFReadStreamRef stream, CFStreamError *error, void *info) {
_CFHTTPRequest *req = (_CFHTTPRequest *)info;
int currentState;
#if defined(LOG_REQUESTS)
fprintf(stderr, "httpRequestOpenCompleted(req = 0x%x)\n", (int)req);
#endif
if (__CFBitIsSet(req->flags, OPEN_SIGNALLED)) return TRUE;
if (req->proxyStream) {
setConnectionFromProxyStream(req, error);
if (error->domain != 0) return TRUE;
else if (req->proxyStream) return FALSE;
}
if (req->conn) {
currentState = _CFNetConnectionGetState(req->conn, TRUE, req); } else {
currentState = _CFHTTPRequestGetState(req);
}
return (currentState > kQueued);
}
static CFHTTPMessageRef constructRedirectedRequest(CFURLRef newDest, CFHTTPMessageRef origRequest) {
CFStringRef reqMethod = CFHTTPMessageCopyRequestMethod(origRequest);
CFStringRef oldVersion = CFHTTPMessageCopyVersion(origRequest);
CFHTTPMessageRef newRequest = CFHTTPMessageCreateRequest(CFGetAllocator(origRequest), reqMethod, newDest, oldVersion);
CFDictionaryRef reqHeaders;
CFRelease(reqMethod);
CFRelease(oldVersion);
if (!newRequest) return NULL;
reqHeaders = CFHTTPMessageCopyAllHeaderFields(origRequest);
if (reqHeaders) {
CFIndex count = CFDictionaryGetCount(reqHeaders);
if (count > 0) {
CFStringRef *keys, *values, *currentKey, *currentValue;
CFAllocatorRef alloc = CFGetAllocator(origRequest);
keys = CFAllocatorAllocate(alloc, sizeof(CFStringRef) * 2 * count, 0);
values = keys + count;
CFDictionaryGetKeysAndValues(reqHeaders, (const void **)keys, (const void **)values);
for (currentKey = keys, currentValue = values; currentKey < values; currentValue ++, currentKey ++) {
CFHTTPMessageSetHeaderFieldValue(newRequest, *currentKey, *currentValue);
}
CFAllocatorDeallocate(alloc, keys);
}
CFRelease(reqHeaders);
}
cleanUpRequest(newRequest, -1, false, false);
return newRequest;
}
#define DONE (-100)
#define EXTRA_RESPONSE (100)
#define OK (200)
#define REDIRECT (300)
#define AUTHENTICATE (401)
#define PROXY_AUTHENTICATE (407)
static int nextActionForHeaders(CFHTTPMessageRef headers, _CFHTTPRequest *http, CFURLRef *nextURL, Boolean *connectionStaysPersistent) {
CFStringRef requestMethod = CFHTTPMessageCopyRequestMethod(http->originalRequest);
int status = CFHTTPMessageGetResponseStatusCode(headers);
int result;
*connectionStaysPersistent = canKeepAlive(headers, http->currentRequest);
if ((status >= 100 && status <200) || status == 204 || status == 304 || CFEqual(_kCFHTTPStreamHEADMethod, requestMethod)) {
CFRelease(requestMethod);
__CFBitSet(http->flags, HAVE_CHECKED_RESPONSE_HEADERS);
return DONE;
}
CFRelease(requestMethod);
if (__CFBitIsSet(http->flags, AUTOREDIRECT) && status >= 300 && status < 400) {
CFStringRef newLocation = CFHTTPMessageCopyHeaderFieldValue(headers, _kCFHTTPStreamLocationHeader);
CFURLRef lastURL = CFArrayGetValueAtIndex(http->redirectedURLs, CFArrayGetCount(http->redirectedURLs) - 1);
if (newLocation) {
CFAllocatorRef alloc = CFGetAllocator(headers);
CFRange commaRg = CFStringFind(newLocation, _kCFHTTPStreamLocationSeparator, 0);
if (commaRg.location != kCFNotFound) {
CFStringRef substr = CFStringCreateWithSubstring(alloc, newLocation, CFRangeMake(0, commaRg.location));
*nextURL = CFURLCreateWithString(alloc, substr, lastURL);
CFRelease(substr);
} else {
*nextURL = CFURLCreateWithString(alloc, newLocation, lastURL);
}
CFRelease(newLocation);
} else {
*nextURL = NULL;
}
if (*nextURL) {
result = REDIRECT;
} else {
result = OK;
}
} else {
result = OK;
}
if (result == OK) {
__CFBitSet(http->flags, HAVE_CHECKED_RESPONSE_HEADERS);
}
*connectionStaysPersistent = canKeepAlive(headers, http->currentRequest);
return result;
}
static void addAuthenticationInfoToResponse1(_CFHTTPRequest *http) {
CFURLRef requestedURL;
_CFNetConnectionRef conn = http->conn;
Boolean persistent = _CFNetConnectionWillEnqueueRequests(conn);
CFHTTPAuthenticationRef auth = NULL;
if (__CFBitIsSet(http->flags, AUTOREDIRECT)) {
requestedURL = CFArrayGetValueAtIndex(http->redirectedURLs, CFArrayGetCount(http->redirectedURLs) - 1);
CFRetain(requestedURL);
} else {
requestedURL = CFHTTPMessageCopyRequestURL(http->originalRequest);
}
_CFHTTPMessageSetResponseURL(http->responseHeaders, requestedURL);
CFRelease(requestedURL);
auth = _CFHTTPMessageGetAuthentication(http->originalRequest, FALSE);
if (auth) {
_CFHTTPAuthenticationUpdateFromResponse(auth, http->responseHeaders, conn);
if (!persistent)
_CFHTTPAuthenticationDisassociateConnection(auth, conn);
}
auth = _CFHTTPMessageGetAuthentication(http->originalRequest, TRUE);
if (auth) {
_CFHTTPAuthenticationUpdateFromResponse(auth, http->responseHeaders, conn);
if (!persistent)
_CFHTTPAuthenticationDisassociateConnection(auth, conn);
}
}
static Boolean checkHeaders(_CFHTTPRequest *http, CFReadStreamRef stream, CFStreamError *error, Boolean *connectionStaysPersistent) {
CFURLRef nextURL = NULL;
Boolean result = TRUE;
int nextAction;
if (!stream) {
CFLog(0, CFSTR("Internal consistency check error for http request 0x%x"), http);
error->domain = kCFStreamErrorDomainHTTP;
error->error = -1;
return TRUE;
}
error->error = 0;
if (http->responseHeaders) CFRelease(http->responseHeaders);
http->responseHeaders = (CFHTTPMessageRef)CFReadStreamCopyProperty(stream, kCFStreamPropertyHTTPResponseHeader);
if (!http->responseHeaders) {
nextAction = OK;
*connectionStaysPersistent = FALSE;
__CFBitSet(http->flags, HAVE_CHECKED_RESPONSE_HEADERS);
} else {
nextAction = nextActionForHeaders(http->responseHeaders, http, &nextURL, connectionStaysPersistent); }
if (!error->error && isPersistent(http)) {
if (!*connectionStaysPersistent) {
_CFNetConnectionLost(http->conn);
#if !defined(NO_PIPELINING)
} else {
_CFNetConnectionSetShouldPipeline(http->conn, TRUE);
#endif
}
}
if (http->responseHeaders)
addAuthenticationInfoToResponse1(http);
switch (nextAction) {
case DONE:
__CFBitSet(http->flags, FORCE_EOF);
break;
case REDIRECT:
if (!http->firstRedirection) {
http->firstRedirection = http->responseHeaders;
CFRetain(http->firstRedirection);
}
if (!CFArrayContainsValue(http->redirectedURLs, CFRangeMake(0, CFArrayGetCount(http->redirectedURLs)), nextURL)) {
CFHTTPMessageRef newRequest = constructRedirectedRequest(nextURL, http->currentRequest);
CFArrayAppendValue(http->redirectedURLs, nextURL);
dequeueFromConnection1(http);
closeRequestResources1(http);
CFRelease(http->proxyList);
http->proxyList = NULL;
if (resetForRequest(newRequest, http, error)) {
result = FALSE;
}
CFRelease(newRequest);
} else {
error->domain = kCFStreamErrorDomainHTTP;
error->error = kCFStreamErrorHTTPRedirectionLoop;
}
break;
case AUTHENTICATE:
case PROXY_AUTHENTICATE:
case OK:
default:
;
}
if (nextURL) {
CFRelease(nextURL);
}
return result;
}
static CFIndex readFromConnection(_CFHTTPRequest *req, UInt8 *buffer, CFIndex length, Boolean *atEOF, CFStreamError *error) {
Boolean readFromThisStream = TRUE; CFReadStreamRef stream = _CFNetConnectionGetResponseStream(req->conn);
_CFNetConnectionRef connWeAreDoneWith = NULL;
CFIndex result;
*atEOF = FALSE;
error->error = 0;
if (_CFHTTPReadStreamIsAtMark(stream)) {
result = 0;
*atEOF = TRUE;
connWeAreDoneWith = req->conn;
CFRetain(connWeAreDoneWith);
} else {
result = CFReadStreamRead(stream, buffer, length);
if (result < 0) {
*error = CFReadStreamGetError(stream);
if (error->domain != kCFStreamErrorDomainHTTP || error->error != kCFStreamErrorHTTPConnectionLost) {
__CFBitSet(req->flags, DO_NOT_REATTEMPT);
}
_CFNetConnectionErrorOccurred(req->conn, error);
return -1;
} else if (kCFStreamStatusAtEnd == CFReadStreamGetStatus(stream) || _CFHTTPReadStreamIsAtMark(stream)) {
connWeAreDoneWith = req->conn;
CFRetain(connWeAreDoneWith);
*atEOF = TRUE;
}
}
if (!haveCheckedHeaders(req)) {
Boolean connectionStaysPersistent;
readFromThisStream = checkHeaders(req, stream, error, &connectionStaysPersistent);
if (isPersistent(req)) {
if (!connectionStaysPersistent) {
_CFNetConnectionLost(req->conn);
#if !defined(NO_PIPELINING)
} else {
_CFNetConnectionSetShouldPipeline(req->conn, TRUE);
#endif
}
}
}
if (error->error == 0) {
if (!readFromThisStream) {
result = httpRequestRead(req->responseStream, buffer, length, error, atEOF, req);
} else if (__CFBitIsSet(req->flags, FORCE_EOF)) {
connWeAreDoneWith = req->conn;
result = 0;
}
}
if (connWeAreDoneWith) {
_CFNetConnectionResponseIsComplete(connWeAreDoneWith, req);
CFRelease(connWeAreDoneWith);
}
return result;
}
extern void emptyPerform(void *info) {
}
static CFIndex httpRequestRead(CFReadStreamRef stream, UInt8 *buffer, CFIndex bufferLength, CFStreamError *error, Boolean *atEOF, void *info) {
CFIndex result;
_CFHTTPRequest *req = (_CFHTTPRequest *)info;
_CFNetConnectionRef oldConn;
enum _CFNetConnectionState state;
#if defined(LOG_REQUESTS)
fprintf(stderr, "httpRequestRead(req = 0x%x)\n", (int)req);
#endif
if (req->proxyStream) {
setConnectionFromProxyStream(req, error);
if (error->domain != 0) {
return -1;
}
if (req->proxyStream) {
CFRunLoopRef currentRL = CFRunLoopGetCurrent();
CFStringRef mode = _kCFHTTPStreamPrivateRunLoopMode;
CFReadStreamScheduleWithRunLoop(req->proxyStream, currentRL, mode);
__CFBitSet(req->flags, WAITING_FOR_PROXY_STREAM);
while (__CFBitIsSet(req->flags, WAITING_FOR_PROXY_STREAM)) {
CFRunLoopRunInMode(mode, 1e+20, TRUE);
}
setConnectionFromProxyStream(req, error);
if (error->domain != 0) {
return -1;
}
}
}
oldConn = req->conn; __CFBitSet(req->flags, IN_READ_CALLBACK);
state = oldConn ? _CFNetConnectionGetState(oldConn, TRUE, req) : -1;
if (state < kReceivingResponse) {
CFRunLoopRef currentRL = CFRunLoopGetCurrent();
CFStringRef mode = _kCFHTTPStreamPrivateRunLoopMode;
CFReadStreamScheduleWithRunLoop(stream, currentRL, mode);
if (req->requestPayload) {
CFReadStreamScheduleWithRunLoop(req->requestPayload, currentRL, mode);
}
CFRetain(oldConn);
if (!req->stateChangeSource) {
CFRunLoopSourceContext rlsCtxt = {0, req, NULL, NULL, NULL, NULL, NULL, NULL, NULL, emptyPerform};
req->stateChangeSource = CFRunLoopSourceCreate(CFGetAllocator(stream), 0, &rlsCtxt);
}
CFRunLoopAddSource(currentRL, req->stateChangeSource, mode);
state = _CFNetConnectionGetState(oldConn, TRUE, req);
while ((oldConn == req->conn) && state < kReceivingResponse) {
CFRunLoopRunInMode(mode, 1e+20, TRUE);
state = _CFNetConnectionGetState(oldConn, TRUE, req);
if (state >= kReceivingResponse && oldConn != req->conn && req->conn) {
CFRelease(oldConn);
oldConn = req->conn;
CFRetain(oldConn);
state = kQueued;
}
}
CFReadStreamUnscheduleFromRunLoop(stream, currentRL, mode);
if (req->requestPayload) {
CFReadStreamUnscheduleFromRunLoop(req->requestPayload, currentRL, mode);
}
CFRelease(oldConn);
CFRunLoopRemoveSource(currentRL, req->stateChangeSource, mode);
}
__CFBitClear(req->flags, IN_READ_CALLBACK);
if (state == kFinished) {
error->error = 0;
*atEOF = TRUE;
result = 0;
} else if (state == kOrphaned || !req->conn) {
*error = CFReadStreamGetError(stream);
if (error->error == 0) {
error->error = ECONNRESET;
error->domain = _kCFStreamErrorDomainNativeSockets;
}
result = -1;
} else {
oldConn = req->conn;
result = readFromConnection(req, buffer, bufferLength, atEOF, error);
if (result < 0 && req->conn != oldConn && req->conn != NULL) {
result = httpRequestRead(stream, buffer, bufferLength, error, atEOF, info);
}
}
return result;
}
static Boolean httpRequestCanRead(CFReadStreamRef ourStream, void *info) {
_CFHTTPRequest *req = (_CFHTTPRequest *)info;
int state;
CFReadStreamRef stream;
#if defined(LOG_REQUESTS)
fprintf(stderr, "httpRequestCanRead(req = 0x%x)\n", (int)req);
#endif
if (req->proxyStream) {
CFStreamError err;
setConnectionFromProxyStream(req, &err);
if (err.domain != 0) {
CFReadStreamSignalEvent(req->responseStream, kCFStreamEventErrorOccurred, &err);
return FALSE;
}
}
if (req->proxyStream) {
return FALSE;
}
if (req->conn) {
state = _CFNetConnectionGetState(req->conn, TRUE, req); if (!req->conn && state <= kReceivingResponse) {
CFLog(0, CFSTR("Detected bad return from CFNetConnectionGetState"));
state = _CFHTTPRequestGetState(req);
}
} else {
state = _CFHTTPRequestGetState(req);
}
if (state > kReceivingResponse) {
return TRUE;
} else if (state < kReceivingResponse) {
return FALSE;
}
stream = _CFNetConnectionGetResponseStream(req->conn);
if (!CFReadStreamHasBytesAvailable(stream)) {
return FALSE;
} else if (__CFBitIsSet(req->flags, HAVE_CHECKED_RESPONSE_HEADERS)) {
return TRUE;
} else {
CFStreamError err = {0, 0};
Boolean persistentOK;
Boolean useThisStream = checkHeaders(req, stream, &err, &persistentOK);
if (err.error != 0) {
if (err.domain != kCFStreamErrorDomainHTTP || err.error != kCFStreamErrorHTTPConnectionLost) {
__CFBitSet(req->flags, DO_NOT_REATTEMPT);
}
_CFNetConnectionErrorOccurred(req->conn, &err);
} else if (isPersistent(req)) {
if (!persistentOK) {
_CFNetConnectionLost(req->conn);
#if !defined(NO_PIPELINING)
} else {
_CFNetConnectionSetShouldPipeline(req->conn, TRUE);
#endif
}
}
if (useThisStream) {
return TRUE;
}
}
return FALSE;
}
static void httpRequestClose(CFReadStreamRef stream, void *info) {
_CFHTTPRequest *req = (_CFHTTPRequest *)info;
#if defined(LOG_REQUESTS)
fprintf(stderr, "httpRequestClose(req = 0x%x)\n", (int)req);
#endif
if (req->conn) {
dequeueFromConnection1(req);
}
if (req->proxyStream) {
CFReadStreamClose(req->proxyStream);
CFRelease(req->proxyStream);
req->proxyStream = NULL;
}
}
static CFTypeRef httpRequestCopyProperty(CFReadStreamRef stream, CFStringRef propertyName, void *info) {
_CFHTTPRequest *req = (_CFHTTPRequest *)info;
CFTypeRef property = NULL;
#if defined(LOG_REQUESTS)
fprintf(stderr, "httpRequestCopyProperty(req = 0x%x)\n", (int)req);
#endif
if (CFEqual(propertyName, kCFStreamPropertyHTTPResponseHeader)) {
property = req->responseHeaders;
if (property) CFRetain(property);
} else if (CFEqual(propertyName, kCFStreamPropertySSLPeerCertificates)) {
if (req->peerCertificates)
property = CFRetain(req->peerCertificates);
else if (req->conn) {
CFReadStreamRef rStream = _CFNetConnectionGetResponseStream(req->conn);
if (rStream) {
property = CFReadStreamCopyProperty(rStream, propertyName);
}
if (!property) {
CFWriteStreamRef wStream = _CFNetConnectionGetRequestStream(req->conn);
if (wStream) {
property = CFWriteStreamCopyProperty(wStream, propertyName);
}
}
}
} else if (CFEqual(propertyName, kCFStreamPropertyHTTPFinalURL)) {
if (req->currentRequest) {
property = CFHTTPMessageCopyRequestURL(req->currentRequest);
} else {
property = CFHTTPMessageCopyRequestURL(req->originalRequest);
}
} else if (CFEqual(propertyName, kCFStreamPropertyHTTPRequest)) {
static Boolean warnOnce = FALSE;
if (!warnOnce) {
CFLog(0, CFSTR("Use of kCFStreamPropertyHTTPRequest is deprecated; please use kCFStreamPropertyHTTPFinalURL instead"));
warnOnce = TRUE;
}
property = req->currentRequest;
if (property) CFRetain(property);
} else if (CFEqual(propertyName, kCFStreamPropertyHTTPProxy)) {
property = req->proxyDict;
if (property) CFRetain(property);
} else if (CFEqual(propertyName, kCFHTTPRedirectionResponse)) {
property = req->firstRedirection;
if (property) CFRetain(property);
} else if (CFEqual(propertyName, kCFStreamPropertyHTTPRequestBytesWrittenCount)) {
property = CFNumberCreate(CFGetAllocator(stream), kCFNumberLongLongType, &(req->requestBytesWritten));
} else if (req->conn) {
CFReadStreamRef rStream = _CFNetConnectionGetResponseStream(req->conn);
if (rStream) {
property = CFReadStreamCopyProperty(rStream, propertyName);
}
if (!property) {
CFWriteStreamRef wStream = _CFNetConnectionGetRequestStream(req->conn);
if (wStream) {
property = CFWriteStreamCopyProperty(wStream, propertyName);
}
}
} else {
property = NULL;
}
return property;
}
static Boolean httpRequestSetProperty(CFReadStreamRef stream, CFStringRef propertyName, CFTypeRef propertyValue, void *info) {
_CFHTTPRequest *http = (_CFHTTPRequest *)info;
#if defined(LOG_REQUESTS)
fprintf(stderr, "httpRequestSetProperty(req = 0x%x)\n", (int)http);
#endif
if (CFReadStreamGetStatus(stream) > kCFStreamStatusNotOpen) return FALSE;
if (CFEqual(propertyName, kCFStreamPropertyHTTPShouldAutoredirect)) {
if (propertyValue == kCFBooleanTrue) {
if (!http->redirectedURLs) {
http->redirectedURLs = CFArrayCreateMutable(CFGetAllocator(stream),0, &kCFTypeArrayCallBacks);
CFArrayAppendValue(http->redirectedURLs, CFHTTPMessageCopyRequestURL(http->originalRequest));
CFRelease((CFTypeRef)CFArrayGetValueAtIndex(http->redirectedURLs, 0));
}
__CFBitSet(http->flags, AUTOREDIRECT);
return TRUE;
} else if (propertyValue == kCFBooleanFalse) {
if (http->redirectedURLs) {
CFRelease(http->redirectedURLs);
http->redirectedURLs = NULL;
}
__CFBitClear(http->flags, AUTOREDIRECT);
return TRUE;
} else {
return FALSE;
}
} else if (CFEqual(propertyName, kCFStreamPropertyHTTPProxy)) {
if (propertyValue == NULL) {
if (http->proxyDict) {
CFRelease(http->proxyDict);
http->proxyDict = NULL;
}
return TRUE;
} else if (CFGetTypeID(propertyValue) != CFDictionaryGetTypeID()) {
return FALSE;
} else {
if (http->proxyDict) CFRelease(http->proxyDict);
http->proxyDict = CFDictionaryCreateCopy(CFGetAllocator(stream), propertyValue);
return TRUE;
}
} else if (CFEqual(propertyName, _kCFStreamPropertyHTTPProxyProxyAutoConfigURLString)) {
if (propertyValue == NULL) {
if (http->proxyDict && CFDictionaryGetValue(http->proxyDict, _kCFStreamPropertyHTTPProxyProxyAutoConfigURLString)) {
CFMutableDictionaryRef mDict = CFDictionaryCreateMutableCopy(CFGetAllocator(stream), CFDictionaryGetCount(http->proxyDict), http->proxyDict);
CFDictionaryRemoveValue(mDict, _kCFStreamPropertyHTTPProxyProxyAutoConfigURLString);
CFRelease(http->proxyDict);
http->proxyDict = mDict;
}
} else {
if (http->proxyDict) {
CFMutableDictionaryRef mDict = CFDictionaryCreateMutableCopy(CFGetAllocator(stream), CFDictionaryGetCount(http->proxyDict) + 1, http->proxyDict);
CFDictionarySetValue(mDict, _kCFStreamPropertyHTTPProxyProxyAutoConfigURLString, propertyValue);
CFRelease(http->proxyDict);
http->proxyDict = mDict;
} else {
http->proxyDict = CFDictionaryCreate(CFGetAllocator(stream), (const void **)(&_kCFStreamPropertyHTTPProxyProxyAutoConfigURLString), &propertyValue, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
}
}
return TRUE;
} else if (CFEqual(propertyName, kCFStreamPropertySOCKSProxy)) {
if (!propertyValue) {
if (http->proxyDict && CFDictionaryGetValue(http->proxyDict, kCFStreamPropertySOCKSProxyHost)) {
CFMutableDictionaryRef mDict = CFDictionaryCreateMutableCopy(CFGetAllocator(stream), CFDictionaryGetCount(http->proxyDict), http->proxyDict);
CFDictionaryRemoveValue(mDict, kCFStreamPropertySOCKSProxyHost);
CFDictionaryRemoveValue(mDict, kCFStreamPropertySOCKSProxyPort);
CFRelease(http->proxyDict);
http->proxyDict = mDict;
}
} else if (CFGetTypeID(propertyValue) != CFDictionaryGetTypeID()) {
return FALSE;
} else {
CFDictionaryRef dict = (CFDictionaryRef)propertyValue;
CFTypeRef keys[NUM_SOCKS_PROPS], values[NUM_SOCKS_PROPS];
CFIndex numEntries = extractSocksProperties(dict, keys, values);
if (!http->proxyDict && numEntries) {
http->proxyDict = CFDictionaryCreate(CFGetAllocator(stream), keys, values, numEntries, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
} else {
CFMutableDictionaryRef mDict = CFDictionaryCreateMutableCopy(CFGetAllocator(stream), CFDictionaryGetCount(http->proxyDict) + 4, http->proxyDict);
CFIndex i;
for (i = 0; i < numEntries; i ++) {
CFDictionarySetValue(mDict, keys[i], values[i]);
}
CFRelease(http->proxyDict);
http->proxyDict = mDict;
}
}
return TRUE;
} else if (CFEqual(propertyName, kCFStreamPropertyHTTPAttemptPersistentConnection)) {
if (propertyValue == kCFBooleanTrue) {
if (!isPersistent(http)) __CFBitSet(http->flags, IS_PERSISTENT);
return TRUE;
} else if (propertyValue == kCFBooleanFalse) {
if (isPersistent(http)) __CFBitClear(http->flags, IS_PERSISTENT);
return TRUE;
} else {
return FALSE;
}
} else if (CFEqual(propertyName, kCFStreamPropertySocketSecurityLevel) ||
CFEqual(propertyName, kCFStreamPropertyShouldCloseNativeSocket)) {
return FALSE;
} else if (CFEqual(propertyName, _kCFStreamPropertyHTTPConnectionStreams)) {
if (CFGetTypeID(propertyValue) != CFArrayGetTypeID()
|| CFArrayGetCount(propertyValue) != 2
|| CFGetTypeID(CFArrayGetValueAtIndex(propertyValue, 0)) != CFReadStreamGetTypeID()
|| CFGetTypeID(CFArrayGetValueAtIndex(propertyValue, 1)) != CFWriteStreamGetTypeID()) {
return FALSE;
} else {
__CFBitSet(http->flags, CUSTOM_STREAMS);
CFDictionarySetValue(http->connProps, propertyName, propertyValue);
return TRUE;
}
} else {
if (propertyValue) {
CFDictionarySetValue(http->connProps, propertyName, propertyValue);
} else {
CFDictionaryRemoveValue(http->connProps, propertyName);
}
return TRUE;
}
}
static void httpRequestSchedule(CFReadStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info) {
_CFHTTPRequest *req = (_CFHTTPRequest *)info;
#if defined(LOG_REQUESTS)
fprintf(stderr, "httpRequestSchedule(req = 0x%x)\n", (int)req);
#endif
if (_CFHTTPRequestGetState(req) < kFinished) {
if (req->conn) {
_CFNetConnectionSchedule(req->conn, req, runLoop, runLoopMode);
}
if (req->proxyStream) {
CFReadStreamScheduleWithRunLoop(req->proxyStream, runLoop, runLoopMode);
}
}
}
static void httpRequestUnschedule(CFReadStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info) {
_CFHTTPRequest *req = (_CFHTTPRequest *)info;
#if defined(LOG_REQUESTS)
fprintf(stderr, "httpRequestUnschedule(req = 0x%x)\n", (int)req);
#endif
if (_CFHTTPRequestGetState(req) < kFinished) {
if (req->conn) {
_CFNetConnectionUnschedule(req->conn, req, runLoop, runLoopMode);
}
if (req->proxyStream) {
CFReadStreamUnscheduleFromRunLoop(req->proxyStream, runLoop, runLoopMode);
}
}
}
static void requestPayloadCallBack(CFReadStreamRef stream, CFStreamEventType type, void *info) {
_CFHTTPRequest *req = (_CFHTTPRequest *)info;
#if defined(LOG_REQUESTS)
fprintf(stderr, "requestPayloadCallBack(req = 0x%x, event = %d)\n", (int)req, type);
#endif
switch (type) {
case kCFStreamEventEndEncountered:
case kCFStreamEventHasBytesAvailable:
{
CFStreamError err;
CFWriteStreamRef requestStream = _CFNetConnectionGetRequestStream(req->conn);
if (requestStream && transmitRequest1(req, requestStream, &err, FALSE)) {
if (err.error == 0) {
_CFNetConnectionRequestIsComplete(req->conn, req);
} else {
if (err.domain != kCFStreamErrorDomainHTTP || err.error != kCFStreamErrorHTTPConnectionLost) {
__CFBitSet(req->flags, DO_NOT_REATTEMPT);
}
_CFNetConnectionErrorOccurred(req->conn, &err); }
}
break;
}
case kCFStreamEventErrorOccurred: {
CFStreamError err = CFReadStreamGetError(stream);
if (err.domain != kCFStreamErrorDomainHTTP || err.error != kCFStreamErrorHTTPConnectionLost) {
__CFBitSet(req->flags, DO_NOT_REATTEMPT);
}
_CFNetConnectionErrorOccurred(req->conn, &err);
break;
}
default:
;
}
}
static Boolean hasTokenInList(CFStringRef list, CFStringRef token) {
CFIndex len = CFStringGetLength(list);
CFRange rg = {0, len};
while (rg.location < rg.length && CFStringFindWithOptions(list, token, rg, kCFCompareCaseInsensitive, &rg)) {
CFIndex loc = rg.location-1;
UniChar ch = '\0';
while (loc >= 0 && (ch = CFStringGetCharacterAtIndex(list, loc)) && (ch == ' ' || ch == '\t')) {
loc --;
}
if (loc == 0 || ch == ',') {
loc = rg.location + rg.length;
while (loc < len && ch == CFStringGetCharacterAtIndex(list, loc) && (ch == ' ' || ch == '\t')) {
loc ++;
}
if (loc == len || ch == ',') {
return TRUE;
}
}
rg.location = rg.location + rg.length;
rg.length = len - rg.location;
}
return FALSE;
}
extern Boolean canKeepAlive(CFHTTPMessageRef responseHeaders, CFHTTPMessageRef request) {
CFStringRef connectionHeader;
Boolean result;
if (!responseHeaders) {
return FALSE;
}
connectionHeader = CFHTTPMessageCopyHeaderFieldValue(responseHeaders, _kCFHTTPStreamProxyConnectionHeader);
if (!connectionHeader) {
connectionHeader = CFHTTPMessageCopyHeaderFieldValue(responseHeaders, _kCFHTTPStreamConnectionHeader);
}
if (connectionHeader) {
if (CFStringCompare(connectionHeader, _kCFHTTPStreamConnectionClose, kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
result = FALSE;
} else if (CFStringCompare(connectionHeader, _kCFHTTPStreamConnectionKeepAlive, kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
result = TRUE;
} else if (CFStringFind(connectionHeader, _kCFHTTPStreamConnectionSeparator, 0).location == kCFNotFound) {
result = FALSE;
} else {
if (hasTokenInList(connectionHeader, _kCFHTTPStreamConnectionClose)) {
result = FALSE;
} else if (hasTokenInList(connectionHeader, _kCFHTTPStreamConnectionKeepAlive)) {
result = TRUE;
} else {
result = FALSE;
}
}
CFRelease(connectionHeader);
} else {
CFStringRef responseVersion = CFHTTPMessageCopyVersion(responseHeaders);
if (!responseVersion) {
result = FALSE;
} else {
CFStringRef requestVersion = CFHTTPMessageCopyVersion(request);
if (!requestVersion || CFEqual(responseVersion, kCFHTTPVersion1_0) || CFEqual(requestVersion, kCFHTTPVersion1_0)) {
result = FALSE;
} else if (CFEqual(responseVersion, kCFHTTPVersion1_1)) {
result = TRUE;
} else {
int len = CFStringGetLength(responseVersion);
if (len > 6) {
CFStringRef versNum = CFStringCreateWithSubstring(CFGetAllocator(responseVersion), responseVersion, CFRangeMake(5, len-5));
double versValue = CFStringGetDoubleValue(versNum);
result = (versValue > 1.1);
CFRelease(versNum);
} else {
result = FALSE; }
}
CFRelease(responseVersion);
if (requestVersion) CFRelease(requestVersion);
}
}
return result;
}
void httpResponseStreamCallBack(void *theReq, CFReadStreamRef stream, CFStreamEventType type, _CFNetConnectionRef conn, const void* key) {
_CFHTTPRequest *req = (_CFHTTPRequest *)theReq;
Boolean justReadMark = FALSE;
#if defined(LOG_REQUESTS)
fprintf(stderr, "responseStreamCallBack(req = 0x%x, event = %d)\n", (int)req, type);
#endif
if (!__CFBitIsSet(req->flags, HAVE_READ_MARK)) {
justReadMark = TRUE;
prepareReception1(req, stream);
}
switch (type) {
case kCFStreamEventHasBytesAvailable:
if (justReadMark) break; if (haveCheckedHeaders(req)) {
if (!__CFBitIsSet(req->flags, IS_ZOMBIE)) {
_CFReadStreamSignalEventDelayed(req->responseStream, kCFStreamEventHasBytesAvailable, NULL);
} else {
UInt8 buf[BUF_SIZE];
while (CFReadStreamHasBytesAvailable(stream) > 0) {
CFReadStreamRead(stream, buf, BUF_SIZE);
}
}
} else {
CFStreamError err;
Boolean persistentOK;
Boolean useThisStream = checkHeaders(req, stream, &err, &persistentOK);
if (err.error != 0) {
_CFReadStreamSignalEventDelayed(req->responseStream, kCFStreamEventErrorOccurred, &err);
} else {
if (useThisStream) {
if (__CFBitIsSet(req->flags, FORCE_EOF)) {
_CFNetConnectionResponseIsComplete(req->conn, req);
} else if (!__CFBitIsSet(req->flags, IS_ZOMBIE)) {
_CFReadStreamSignalEventDelayed(req->responseStream, kCFStreamEventHasBytesAvailable, NULL);
} else {
UInt8 buf[BUF_SIZE];
while (CFReadStreamHasBytesAvailable(stream) > 0) {
CFReadStreamRead(stream, buf, BUF_SIZE);
}
}
}
}
}
break;
case kCFStreamEventMarkEncountered:
if (!justReadMark && req->conn) {
_CFNetConnectionResponseIsComplete(req->conn, req);
}
break;
case kCFStreamEventEndEncountered:
if (isPersistent(req)) {
_CFNetConnectionLost(req->conn); }
if (!__CFBitIsSet(req->flags, IS_ZOMBIE)) {
_CFReadStreamSignalEventDelayed(req->responseStream, kCFStreamEventEndEncountered, NULL);
}
break;
case kCFStreamEventErrorOccurred: {
CFStreamError err = CFReadStreamGetError(stream);
if (err.domain != kCFStreamErrorDomainHTTP || err.error != kCFStreamErrorHTTPConnectionLost) {
__CFBitSet(req->flags, DO_NOT_REATTEMPT);
}
if (!req->peerCertificates) {
req->peerCertificates = (CFArrayRef)CFReadStreamCopyProperty(stream, kCFStreamPropertySSLPeerCertificates);
}
_CFNetConnectionErrorOccurred(req->conn, &err);
break;
}
default:
;
}
}
void httpRequestStreamCallBack(void *info, CFWriteStreamRef stream, CFStreamEventType type, _CFNetConnectionRef conn, const void* key) {
_CFHTTPRequest *req = (_CFHTTPRequest *)info;
#if defined(LOG_REQUESTS)
fprintf(stderr, "requestStreamCallBack(req = 0x%x, event = %d)\n", (int)req, type);
#endif
switch (type) {
case kCFStreamEventCanAcceptBytes: {
CFStreamError err;
if (transmitRequest1(req, stream, &err, FALSE)) {
if (err.error == 0) {
_CFNetConnectionRequestIsComplete(req->conn, req);
} else {
if (err.domain != kCFStreamErrorDomainHTTP || err.error != kCFStreamErrorHTTPConnectionLost) {
__CFBitSet(req->flags, DO_NOT_REATTEMPT);
}
_CFNetConnectionErrorOccurred(req->conn, &err); }
}
break;
}
case kCFStreamEventErrorOccurred: {
CFStreamError err = CFWriteStreamGetError(stream);
if (err.domain != kCFStreamErrorDomainHTTP || err.error != kCFStreamErrorHTTPConnectionLost) {
__CFBitSet(req->flags, DO_NOT_REATTEMPT);
}
if (!req->peerCertificates)
req->peerCertificates = (CFArrayRef)CFWriteStreamCopyProperty(stream, kCFStreamPropertySSLPeerCertificates);
_CFNetConnectionErrorOccurred(req->conn, &err);
break;
}
case kCFStreamEventEndEncountered: {
CFStreamError err = {_kCFStreamErrorDomainNativeSockets, ECONNRESET};
__CFBitSet(req->flags, DO_NOT_REATTEMPT);
_CFNetConnectionErrorOccurred(req->conn, &err);
break;
}
default:
;
}
}
static CFArrayRef httpRunLoopArrayForRequest(void *request, _CFNetConnectionRef conn, const void* info) {
_CFHTTPRequest *req = (_CFHTTPRequest *)request;
if (!req->responseStream) return NULL;
return _CFReadStreamGetRunLoopsAndModes(req->responseStream);
}
CF_EXPORT
CFReadStreamRef CFReadStreamCreateForHTTPRequest(CFAllocatorRef alloc, CFHTTPMessageRef request) {
_CFHTTPRequest httpContext;
memset(&httpContext, 0, sizeof(_CFHTTPRequest));
httpContext.originalRequest = request;
httpContext.flags = 0;
return CFReadStreamCreate(alloc, (void *)&_CFHTTPQueuedResponseStreamCallBacks, &httpContext);
}
CF_EXPORT
CFReadStreamRef CFReadStreamCreateForStreamedHTTPRequest(CFAllocatorRef alloc, CFHTTPMessageRef requestHeaders, CFReadStreamRef requestBody) {
_CFHTTPRequest httpContext;
memset(&httpContext, 0, sizeof(_CFHTTPRequest));
httpContext.originalRequest = requestHeaders;
httpContext.requestPayload = requestBody;
return CFReadStreamCreate(alloc, (void *)&_CFHTTPQueuedResponseStreamCallBacks, &httpContext);
}
CF_EXPORT
void CFHTTPReadStreamSetRedirectsAutomatically(CFReadStreamRef stream, Boolean shouldAutoRedirect) {
static Boolean warnOnce = FALSE;
if (!warnOnce) {
warnOnce = TRUE;
CFLog(0, CFSTR("Usage of CFHTTPReadStreamSetRedirectsAutomatically is deprecated; call SetProperty(kCFStreamPropertyHTTPShouldAutoredirect, kCFBooleanTrue/False) instead"));
}
CFReadStreamSetProperty(stream, kCFStreamPropertyHTTPShouldAutoredirect, shouldAutoRedirect ? kCFBooleanTrue : kCFBooleanFalse);
}
CF_EXPORT
void CFHTTPReadStreamSetProxy(CFReadStreamRef stream, CFStringRef proxyHost, CFIndex proxyPort) {
CFAllocatorRef alloc = CFGetAllocator(stream);
CFNumberRef num = CFNumberCreate(alloc, kCFNumberCFIndexType, &proxyPort);
static Boolean warnOnce = FALSE;
if (!warnOnce) {
warnOnce = TRUE;
CFLog(0, CFSTR("Usage of CFHTTPReadStreamSetProxy is deprecated; call SetProperty(kCFStreamPropertyHTTPProxy) instead"));
}
if (num) {
const void *keys[2], *values[2];
CFDictionaryRef dict;
keys[0] = kCFStreamPropertyHTTPProxyHost;
keys[1] = kCFStreamPropertyHTTPProxyPort;
values[0] = proxyHost;
values[1] = num;
dict = CFDictionaryCreate(alloc, keys, values, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFReadStreamSetProperty(stream, kCFStreamPropertyHTTPProxy, dict);
CFRelease(dict);
CFRelease(num);
}
}
#if defined(__WIN32__)
extern void _CFHTTPStreamCleanup(void) {
__CFSpinLock(&cacheInitLock);
if (httpConnectionCache != NULL) {
releaseConnectionCache(httpConnectionCache);
httpConnectionCache = NULL;
}
__CFSpinUnlock(&cacheInitLock);
}
#endif