#pragma mark Description
#pragma mark -
#pragma mark Includes
#include <CFNetwork/CFServerPriv.h>
#include <CFNetwork/CFHTTPServerPriv.h>
#include <CoreFoundation/CFRuntime.h>
#if !defined(__WIN32__)
#include <sys/types.h>
#include <sys/socket.h>
#else
#include <winsock2.h>
#define SOCK_MAXADDRLEN 255
#endif
#if 0
#pragma mark -
#pragma mark Constant Strings
#endif
#ifdef __CONSTANT_CFSTRINGS__
#define _kCFHTTPServerDescribeFormat CFSTR("<HttpServer 0x%x>{server=%@, connections=%@, info=%@}")
#define _kCFHTTPServerPtrFormat CFSTR("<0x%x>")
#define _kCFHTTPServerContentLengthHeader CFSTR("Content-length")
#define _kCFHTTPServerContentLengthFormat CFSTR("%d")
#define _kCFHTTPServerConnectionDescribeFormat CFSTR("<_HttpConnection 0x%x>{server=0x%x, timer=%@, inStream=%@, outStream=%@, responses=%@, requests=%@, buffered=%@}")
#define _kCFHTTPServerTransferEncodingHeader CFSTR("Transfer-Encoding")
#define _kCFHTTPServerTransferEncodingChunked CFSTR("chunked")
#define _kCFHTTPServerConnectionHeader CFSTR("Connection")
#define _kCFHTTPServerConnectionClose CFSTR("close")
#else
static CONST_STRING_DECL(_kCFHTTPServerDescribeFormat, "<HttpServer 0x%x>{server=%@, connections=%@, info=%@}")
static CONST_STRING_DECL(_kCFHTTPServerPtrFormat, "<0x%x>")
static CONST_STRING_DECL(_kCFHTTPServerContentLengthHeader, "Content-length")
static CONST_STRING_DECL(_kCFHTTPServerContentLengthFormat, "%d")
static CONST_STRING_DECL(_kCFHTTPServerConnectionDescribeFormat, "<_HttpConnection 0x%x>{server=0x%x, timer=%@, inStream=%@, outStream=%@, responses=%@, requests=%@, buffered=%@}")
static CONST_STRING_DECL(_kCFHTTPServerTransferEncodingHeader, "Transfer-Encoding")
static CONST_STRING_DECL(_kCFHTTPServerTransferEncodingChunked, "chunked")
static CONST_STRING_DECL(_kCFHTTPServerConnectionHeader, "Connection")
static CONST_STRING_DECL(_kCFHTTPServerConnectionClose, "close")
#endif
#pragma mark -
#pragma mark Type Declarations
typedef struct {
CFRuntimeBase _base;
_CFServerRef _server;
CFMutableArrayRef _connections;
_CFHTTPServerCallBacks _callbacks; _CFHTTPServerContext _ctxt; } HttpServer;
typedef struct {
CFAllocatorRef _alloc; UInt32 _rc;
HttpServer* _server;
CFDataRef _peer;
CFRunLoopTimerRef _timer;
CFReadStreamRef _inStream; CFWriteStreamRef _outStream;
CFMutableDictionaryRef _responses; CFMutableArrayRef _requests;
CFMutableDataRef _bufferedBytes; } HttpConnection;
#pragma mark -
#pragma mark Static Function Declarations
static void _HttpServerRelease(_CFHTTPServerRef server);
static CFStringRef _HttpServerCopyDescription(_CFHTTPServerRef server);
static HttpConnection* _HttpConnectionCreate(CFAllocatorRef alloc, HttpServer* server, CFSocketNativeHandle s);
static HttpConnection* _HttpConnectionRetain(HttpConnection* connection);
static void _HttpConnectionRelease(HttpConnection* connection);
static CFStringRef _HttpConnectionCopyDescription(HttpConnection* connection);
static void _HttpConnectionHandleRequest(HttpConnection* connection);
static void _HttpConnectionHandleHasBytesAvailable(HttpConnection* connection);
static void _HttpConnectionHandleCanAcceptBytes(HttpConnection* connection);
static void _HttpConnectionHandleErrorOccurred(HttpConnection* connection, const CFStreamError* error);
static void _HttpConnectionHandleTimeOut(HttpConnection* connection);
static const void* _ArrayRetainCallBack(CFAllocatorRef allocator, const HttpConnection* connection);
static void _ArrayReleaseCallBack(CFAllocatorRef allocator, const HttpConnection* connection);
static void _ReadStreamCallBack(CFReadStreamRef inStream, CFStreamEventType type, HttpConnection* connection);
static void _WriteStreamCallBack(CFWriteStreamRef outStream, CFStreamEventType type, HttpConnection* connection);
static void _TimerCallBack(CFRunLoopTimerRef timer, HttpConnection* connection);
static void _HttpServerAddConnection(HttpServer* server, HttpConnection* connection);
static void _HttpServerRemoveConnection(HttpServer* server, HttpConnection* connection);
static void _HttpServerHandleNewConnection(HttpServer* server, CFSocketNativeHandle sock);
static void _HttpServerHandleError(HttpServer* server, const CFStreamError* error);
static void _ServerCallBack(_CFServerRef server, CFSocketNativeHandle sock, const CFStreamError* error, HttpServer* httpServer);
static CFNumberRef _CFNumberCreateWithString(CFAllocatorRef allocator, CFStringRef string);
#if 0
#pragma mark -
#pragma mark Extern Function Declarations
#endif
extern void _CFSocketStreamCreatePair(CFAllocatorRef alloc, CFStringRef host, UInt32 port, CFSocketNativeHandle s,
const CFSocketSignature* sig, CFReadStreamRef* readStream, CFWriteStreamRef* writeStream);
#pragma mark -
#pragma mark Static Variable Definitions
#define kTimeOutInSeconds ((CFTimeInterval)60.0)
#define kBufferSize ((CFIndex)8192)
#define kReadEvents ((CFOptionFlags)(kCFStreamEventHasBytesAvailable | kCFStreamEventErrorOccurred))
#define kWriteEvents ((CFOptionFlags)(kCFStreamEventCanAcceptBytes | kCFStreamEventErrorOccurred))
static CFTypeID _HttpServerTypeId = _kCFRuntimeNotATypeID;
#pragma mark -
#pragma mark Extern Function Definitions (API)
CFTypeID
_CFHTTPServerGetTypeID(void) {
if (_HttpServerTypeId == _kCFRuntimeNotATypeID) {
static const CFRuntimeClass HttpServerClass = {
0, "_CFHTTPServer", NULL, NULL, (void(*)(CFTypeRef))_HttpServerRelease, NULL, NULL, NULL,
(CFStringRef(*)(CFTypeRef))_HttpServerCopyDescription };
_HttpServerTypeId = _CFRuntimeRegisterClass(&HttpServerClass);
}
return _HttpServerTypeId;
}
_CFHTTPServerRef
_CFHTTPServerCreate(CFAllocatorRef alloc, const _CFHTTPServerCallBacks* callbacks, _CFHTTPServerContext* context) {
HttpServer* server = NULL;
do {
_CFServerContext ctxt = {
0,
NULL,
(CFAllocatorRetainCallBack)CFRetain,
(CFAllocatorReleaseCallBack)CFRelease,
(CFAllocatorCopyDescriptionCallBack)CFCopyDescription
};
CFArrayCallBacks arrayCallBacks = {
0,
(CFArrayRetainCallBack)_ArrayRetainCallBack,
(CFArrayReleaseCallBack)_ArrayReleaseCallBack,
(CFArrayCopyDescriptionCallBack)_HttpConnectionCopyDescription,
NULL };
CFTypeID id = _CFHTTPServerGetTypeID();
if (id != _kCFRuntimeNotATypeID) {
server = (HttpServer*)_CFRuntimeCreateInstance(alloc,
id,
sizeof(HttpServer) - sizeof(CFRuntimeBase),
NULL);
}
if (server == NULL)
break;
server->_server = NULL;
server->_connections = NULL;
memset(&server->_callbacks, 0, sizeof(server->_callbacks));
memset(&server->_ctxt, 0, sizeof(server->_ctxt));
ctxt.info = server;
server->_server = _CFServerCreate(alloc, (_CFServerCallBack)_ServerCallBack, &ctxt);
if (server->_server == NULL)
break;
server->_connections = CFArrayCreateMutable(alloc, 0, &arrayCallBacks);
if (server->_connections == NULL)
break;
memcpy(&(server->_callbacks), callbacks, sizeof(server->_callbacks));
memcpy(&(server->_ctxt), context, sizeof(server->_ctxt));
if (server->_ctxt.info && server->_ctxt.retain)
server->_ctxt.info = (void *)(server->_ctxt.retain(server->_ctxt.info));
return (_CFHTTPServerRef)server;
} while (0);
if (server) {
_CFHTTPServerInvalidate((_CFHTTPServerRef)server);
CFRelease((_CFHTTPServerRef)server);
}
return NULL;
}
void
_HttpServerRelease(_CFHTTPServerRef server) {
_CFHTTPServerInvalidate(server);
}
CFStringRef
_HttpServerCopyDescription(_CFHTTPServerRef server) {
CFStringRef info, result, serverDescription = NULL;
HttpServer* s = (HttpServer*)server;
CFAllocatorRef alloc = CFGetAllocator(server);
if (s->_server)
serverDescription = CFCopyDescription(s->_server);
if (s->_ctxt.copyDescription)
info = s->_ctxt.copyDescription(s->_ctxt.info);
else
info = CFStringCreateWithFormat(alloc, NULL, _kCFHTTPServerPtrFormat, (UInt32)(s->_ctxt.info));
result = CFStringCreateWithFormat(alloc,
NULL,
_kCFHTTPServerDescribeFormat,
(UInt32)s,
serverDescription,
s->_connections,
info);
if (serverDescription)
CFRelease(serverDescription);
CFRelease(info);
return result;
}
Boolean
_CFHTTPServerStart(_CFHTTPServerRef server, CFStringRef name, CFStringRef type, UInt32 port) {
HttpServer* s = (HttpServer*)server;
return _CFServerStart(s->_server, name, type, port);
}
void
_CFHTTPServerInvalidate(_CFHTTPServerRef server) {
HttpServer* s = (HttpServer*)server;
if (s->_ctxt.info && s->_ctxt.release)
s->_ctxt.release(s->_ctxt.info);
memset(&(s->_ctxt), 0, sizeof(s->_ctxt));
memset(&s->_callbacks, 0, sizeof(s->_callbacks));
if (s->_connections) {
CFRelease(s->_connections);
s->_connections = NULL;
}
if (s->_server) {
_CFServerInvalidate(s->_server);
CFRelease(s->_server);
s->_server = NULL;
}
}
UInt32
_CFHTTPServerGetPort(_CFHTTPServerRef server) {
return ((HttpServer*)server)->_server ? _CFServerGetPort(((HttpServer*)server)->_server) : 0;
}
CFDataRef
_CFHTTPServerCopyPeerAddressForRequest(_CFHTTPServerRef server, CFHTTPMessageRef request) {
CFIndex i, count;
HttpServer* s = (HttpServer*)server;
count = CFArrayGetCount(s->_connections);
for (i = 0; i < count; i++) {
HttpConnection* c = (HttpConnection*)CFArrayGetValueAtIndex(s->_connections, i);
CFIndex j = CFArrayGetFirstIndexOfValue(c->_requests, CFRangeMake(0, CFArrayGetCount(c->_requests)), request);
if (j != kCFNotFound) {
return (c->_peer == NULL) ? NULL : CFDataCreateCopy(CFGetAllocator(server), c->_peer);
}
}
return NULL;
}
void
_CFHTTPServerAddResponse(_CFHTTPServerRef server, CFHTTPMessageRef request, CFHTTPMessageRef response) {
CFDataRef body;
UInt8* bytes;
CFReadStreamRef stream;
CFIndex length;
CFStringRef contentLength;
CFAllocatorRef alloc = CFGetAllocator(server);
response = CFHTTPMessageCreateCopy(alloc, response);
body = CFHTTPMessageCopyBody(response);
if (body == NULL)
body = CFDataCreate(alloc, NULL, 0);
length = CFDataGetLength(body);
CFHTTPMessageSetBody(response, NULL);
bytes = (UInt8*)CFAllocatorAllocate(alloc, length, 0);
memmove(bytes, CFDataGetBytePtr(body), length);
CFRelease(body);
stream = CFReadStreamCreateWithBytesNoCopy(alloc, bytes, length, alloc);
contentLength = CFHTTPMessageCopyHeaderFieldValue(response, _kCFHTTPServerContentLengthHeader);
if (contentLength == NULL) {
contentLength = CFStringCreateWithFormat(alloc, NULL, _kCFHTTPServerContentLengthFormat, length);
CFHTTPMessageSetHeaderFieldValue(response, _kCFHTTPServerContentLengthHeader, contentLength);
}
CFRelease(contentLength);
_CFHTTPServerAddStreamedResponse(server, request, response, stream);
CFRelease(stream);
CFRelease(response);
}
void
_CFHTTPServerAddStreamedResponse(_CFHTTPServerRef server, CFHTTPMessageRef request, CFHTTPMessageRef response, CFReadStreamRef body) {
CFArrayRef list;
CFIndex i, count;
HttpServer* s = (HttpServer*)server;
CFAllocatorRef alloc = CFGetAllocator(server);
CFTypeRef objs[] = {NULL, body};
objs[0] = CFHTTPMessageCreateCopy(alloc, response);
list = CFArrayCreate(alloc, objs, sizeof(objs) / sizeof(objs[0]), &kCFTypeArrayCallBacks);
count = CFArrayGetCount(s->_connections);
for (i = 0; i < count; i++) {
HttpConnection* c = (HttpConnection*)CFArrayGetValueAtIndex(s->_connections, i);
CFIndex j = CFArrayGetFirstIndexOfValue(c->_requests, CFRangeMake(0, CFArrayGetCount(c->_requests)), request);
if (j != kCFNotFound) {
CFDictionaryAddValue(c->_responses, request, list);
if ((j == 0) && CFWriteStreamCanAcceptBytes(c->_outStream))
_HttpConnectionHandleCanAcceptBytes(c);
break;
}
}
CFRelease(list);
CFRelease(objs[0]);
}
#pragma mark -
#pragma mark Static Function Definitions
HttpConnection*
_HttpConnectionCreate(CFAllocatorRef alloc, HttpServer* server, CFSocketNativeHandle s) {
HttpConnection* connection = NULL;
do {
uint8_t name[SOCK_MAXADDRLEN];
socklen_t namelen = sizeof(name);
CFRunLoopRef rl = CFRunLoopGetCurrent();
CFRunLoopTimerContext timerCtxt = {
0,
NULL,
NULL,
NULL,
(CFStringRef (*)(const void*))_HttpConnectionCopyDescription
};
CFStreamClientContext streamCtxt = {
0,
NULL,
NULL,
NULL,
(CFStringRef (*)(void*))_HttpConnectionCopyDescription
};
connection = CFAllocatorAllocate(alloc, sizeof(connection[0]), 0);
if (connection == NULL)
break;
memset(connection, 0, sizeof(connection[0]));
connection->_alloc = alloc ? CFRetain(alloc) : NULL;
_HttpConnectionRetain(connection);
connection->_server = (HttpServer*)CFRetain((_CFHTTPServerRef)server);
if (0 == getpeername(s, (struct sockaddr *)name, &namelen))
connection->_peer = CFDataCreate(alloc, name, namelen);
timerCtxt.info = connection;
streamCtxt.info = connection;
connection->_timer = CFRunLoopTimerCreate(alloc,
CFAbsoluteTimeGetCurrent() + kTimeOutInSeconds,
kTimeOutInSeconds,
0,
0,
(CFRunLoopTimerCallBack)_TimerCallBack,
&timerCtxt);
if (connection->_timer == NULL)
break;
CFRunLoopAddTimer(rl, connection->_timer, kCFRunLoopCommonModes);
_CFSocketStreamCreatePair(alloc, NULL, 0, s, NULL, &(connection->_inStream), &(connection->_outStream));
if ((connection->_inStream == NULL) || (connection->_outStream == NULL))
break;
CFReadStreamSetProperty(connection->_inStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
CFWriteStreamSetProperty(connection->_outStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
CFReadStreamSetClient(connection->_inStream, kReadEvents, (CFReadStreamClientCallBack)_ReadStreamCallBack, &streamCtxt);
CFWriteStreamSetClient(connection->_outStream, kWriteEvents, (CFWriteStreamClientCallBack)_WriteStreamCallBack, &streamCtxt);
CFReadStreamScheduleWithRunLoop(connection->_inStream, rl, kCFRunLoopCommonModes);
CFWriteStreamScheduleWithRunLoop(connection->_outStream, rl, kCFRunLoopCommonModes);
CFReadStreamOpen(connection->_inStream);
CFWriteStreamOpen(connection->_outStream);
connection->_responses = CFDictionaryCreateMutable(alloc,
0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
if (connection->_responses == NULL)
break;
connection->_requests = CFArrayCreateMutable(alloc,
0,
&kCFTypeArrayCallBacks);
if (connection->_requests == NULL)
break;
connection->_bufferedBytes = CFDataCreateMutable(alloc, 0);
if (connection->_bufferedBytes == NULL)
break;
return connection;
} while (0);
if (connection)
_HttpConnectionRelease(connection);
return NULL;
}
HttpConnection*
_HttpConnectionRetain(HttpConnection* connection) {
connection->_rc++;
return connection;
}
void
_HttpConnectionRelease(HttpConnection* connection) {
connection->_rc--;
if (connection->_rc == 0) {
CFAllocatorRef alloc = connection->_alloc;
CFRunLoopRef runLoop = CFRunLoopGetCurrent();
if (connection->_server)
CFRelease((_CFHTTPServerRef)connection->_server);
if (connection->_peer)
CFRelease(connection->_peer);
if (connection->_inStream) {
CFReadStreamSetClient(connection->_inStream, 0, NULL, NULL);
CFReadStreamUnscheduleFromRunLoop(connection->_inStream, runLoop, kCFRunLoopCommonModes);
CFReadStreamClose(connection->_inStream);
CFRelease(connection->_inStream);
}
if (connection->_outStream) {
CFWriteStreamSetClient(connection->_outStream, 0, NULL, NULL);
CFWriteStreamUnscheduleFromRunLoop(connection->_outStream, runLoop, kCFRunLoopCommonModes);
CFWriteStreamClose(connection->_outStream);
CFRelease(connection->_outStream);
}
if (connection->_timer != NULL) {
CFRunLoopRemoveTimer(runLoop, connection->_timer, kCFRunLoopCommonModes);
CFRunLoopTimerInvalidate(connection->_timer);
CFRelease(connection->_timer);
}
if (connection->_responses)
CFRelease(connection->_responses);
if (connection->_requests)
CFRelease(connection->_requests);
if (connection->_bufferedBytes)
CFRelease(connection->_bufferedBytes);
CFAllocatorDeallocate(alloc, connection);
if (alloc)
CFRelease(alloc);
}
}
CFStringRef
_HttpConnectionCopyDescription(HttpConnection* connection) {
CFStringRef result;
result = CFStringCreateWithFormat(connection->_alloc,
NULL,
_kCFHTTPServerConnectionDescribeFormat,
(UInt32)connection,
(UInt32)connection->_server,
connection->_timer,
connection->_inStream,
connection->_outStream,
connection->_responses,
connection->_requests,
connection->_bufferedBytes);
return result;
}
void
_HttpConnectionHandleRequest(HttpConnection* connection) {
assert(0 != CFArrayGetCount(connection->_requests));
CFHTTPMessageRef msg = (CFHTTPMessageRef)CFArrayGetValueAtIndex(connection->_requests,
CFArrayGetCount(connection->_requests) - 1);
while (msg) {
CFStringRef encoding = CFHTTPMessageCopyHeaderFieldValue(msg, _kCFHTTPServerTransferEncodingHeader);
Boolean chunked = FALSE;
if (encoding) {
chunked = CFStringFindWithOptions(encoding,
_kCFHTTPServerTransferEncodingChunked,
CFRangeMake(0, CFStringGetLength(encoding)),
kCFCompareCaseInsensitive,
NULL);
CFRelease(encoding);
}
if (chunked) {
CFStreamError error = {kCFStreamErrorDomainHTTP, kCFStreamErrorHTTPParseFailure};
_HttpConnectionHandleErrorOccurred(connection, &error);
break;
}
else {
SInt32 size = 0;
CFDataRef body = CFHTTPMessageCopyBody(msg);
CFIndex length = body ? CFDataGetLength(body) : 0;
CFStringRef value = CFHTTPMessageCopyHeaderFieldValue(msg, _kCFHTTPServerContentLengthHeader);
if (value) {
CFNumberRef num = _CFNumberCreateWithString(connection->_alloc, value);
if (num) {
CFNumberGetValue(num, kCFNumberSInt32Type, &size);
CFRelease(num);
}
else {
if (body) CFRelease(body);
CFRelease(value);
CFStreamError error = {kCFStreamErrorDomainHTTP, kCFStreamErrorHTTPParseFailure};
_HttpConnectionHandleErrorOccurred(connection, &error);
break;
}
CFRelease(value);
}
if (length < size) {
if (body) CFRelease(body);
break;
}
else if (length == size) {
if (body) CFRelease(body);
if (connection->_server->_callbacks.didReceiveRequestCallBack != NULL) {
CFRetain(msg);
connection->_server->_callbacks.didReceiveRequestCallBack((_CFHTTPServerRef)connection->_server,
msg,
connection->_server->_ctxt.info);
CFRelease(msg);
}
break;
}
else {
CFDataRef newBody = CFDataCreate(connection->_alloc, CFDataGetBytePtr(body), size);
CFHTTPMessageRef newMsg = CFHTTPMessageCreateEmpty(connection->_alloc, TRUE);
CFHTTPMessageSetBody(msg, newBody);
CFRelease(newBody);
if (connection->_server->_callbacks.didReceiveRequestCallBack != NULL) {
CFRetain(msg);
connection->_server->_callbacks.didReceiveRequestCallBack((_CFHTTPServerRef)connection->_server,
msg,
connection->_server->_ctxt.info);
CFRelease(msg);
}
msg = newMsg;
CFArrayAppendValue(connection->_requests, msg);
CFRelease(msg);
if (!CFHTTPMessageAppendBytes(msg, CFDataGetBytePtr(body) + size, length - size)) {
CFStreamError error = {kCFStreamErrorDomainHTTP, kCFStreamErrorHTTPParseFailure};
_HttpConnectionHandleErrorOccurred(connection, &error);
CFRelease(body);
break;
}
CFRelease(body);
if (!CFHTTPMessageIsHeaderComplete(msg))
break;
else {
Boolean handle = TRUE;
if (connection->_server->_callbacks.acceptNewRequestCallBack) {
handle = connection->_server->_callbacks.acceptNewRequestCallBack((_CFHTTPServerRef)connection->_server,
newMsg,
connection->_peer,
connection->_server->_ctxt.info);
}
if (!handle) {
_HttpServerRemoveConnection(connection->_server, connection);
break;
}
}
}
}
}
}
void
_HttpConnectionHandleHasBytesAvailable(HttpConnection* connection) {
CFIndex bytes;
UInt8 buffer[kBufferSize];
CFHTTPMessageRef msg;
CFIndex i = CFArrayGetCount(connection->_requests);
if (i != 0)
msg = (CFHTTPMessageRef)CFArrayGetValueAtIndex(connection->_requests, --i);
else {
msg = CFHTTPMessageCreateEmpty(connection->_alloc, TRUE);
CFArrayAppendValue(connection->_requests, msg);
CFRelease(msg);
}
bytes = CFReadStreamRead(connection->_inStream, buffer, sizeof(buffer));
if (bytes >= 0) {
Boolean complete = CFHTTPMessageIsHeaderComplete(msg);
CFRunLoopTimerSetNextFireDate(connection->_timer, CFAbsoluteTimeGetCurrent() + kTimeOutInSeconds);
if (!CFHTTPMessageAppendBytes(msg, buffer, bytes)) {
CFStreamError error = {kCFStreamErrorDomainHTTP, kCFStreamErrorHTTPParseFailure};
_HttpConnectionHandleErrorOccurred(connection, &error);
return;
}
if (CFHTTPMessageIsHeaderComplete(msg)) {
Boolean handle = TRUE;
if (!complete && connection->_server->_callbacks.acceptNewRequestCallBack) {
handle = connection->_server->_callbacks.acceptNewRequestCallBack((_CFHTTPServerRef)connection->_server,
msg,
connection->_peer,
connection->_server->_ctxt.info);
}
if (handle)
_HttpConnectionHandleRequest(connection);
else {
_HttpServerRemoveConnection(connection->_server, connection);
}
}
}
}
void
_HttpConnectionHandleCanAcceptBytes(HttpConnection* connection) {
if (CFArrayGetCount(connection->_requests) != 0) {
CFHTTPMessageRef request = (CFHTTPMessageRef)CFArrayGetValueAtIndex(connection->_requests, 0);
CFArrayRef list = request ? (CFArrayRef)CFDictionaryGetValue(connection->_responses, request) : NULL;
CFHTTPMessageRef response = list ? (CFHTTPMessageRef)CFArrayGetValueAtIndex(list, 0) : NULL;
CFReadStreamRef stream = list ? (CFReadStreamRef)CFArrayGetValueAtIndex(list, 1) : NULL;
if (list != NULL) {
CFIndex bytesWritten;
if (CFDataGetLength(connection->_bufferedBytes) == 0) {
CFDataRef serialized = CFHTTPMessageCopySerializedMessage(response);
CFRelease(connection->_bufferedBytes);
connection->_bufferedBytes = CFDataCreateMutableCopy(connection->_alloc, 0, serialized);
CFRelease(serialized);
}
bytesWritten = CFWriteStreamWrite(connection->_outStream,
CFDataGetBytePtr(connection->_bufferedBytes),
CFDataGetLength(connection->_bufferedBytes));
if (bytesWritten > 0) {
CFIndex newSize = CFDataGetLength(connection->_bufferedBytes) - bytesWritten;
CFRunLoopTimerSetNextFireDate(connection->_timer, CFAbsoluteTimeGetCurrent() + kTimeOutInSeconds);
memmove(CFDataGetMutableBytePtr(connection->_bufferedBytes),
CFDataGetBytePtr(connection->_bufferedBytes) + bytesWritten,
newSize);
CFDataSetLength(connection->_bufferedBytes, newSize);
if (newSize == 0) {
CFIndex bytesRead;
if (CFReadStreamGetStatus(stream) == kCFStreamStatusNotOpen)
CFReadStreamOpen(stream);
CFDataSetLength(connection->_bufferedBytes, kBufferSize);
bytesRead = CFReadStreamRead(stream, CFDataGetMutableBytePtr(connection->_bufferedBytes), kBufferSize);
if (bytesRead >= 0)
CFDataSetLength(connection->_bufferedBytes, bytesRead);
if (bytesRead < 0) {
CFStreamError error = CFReadStreamGetError(stream);
_HttpConnectionHandleErrorOccurred(connection, &error);
}
else if (bytesRead == 0) {
CFStringRef close = CFHTTPMessageCopyHeaderFieldValue(response, _kCFHTTPServerConnectionHeader);
CFStringRef version = CFHTTPMessageCopyVersion(response);
if (close == NULL)
close = CFHTTPMessageCopyHeaderFieldValue(request, _kCFHTTPServerConnectionHeader);
if (connection->_server->_callbacks.didSendResponseCallBack != NULL) {
connection->_server->_callbacks.didSendResponseCallBack((_CFHTTPServerRef)connection->_server,
request,
response,
connection->_server->_ctxt.info);
}
CFDictionaryRemoveValue(connection->_responses, request);
CFArrayRemoveValueAtIndex(connection->_requests, 0);
if (((close != NULL) &&
CFStringCompare(close, _kCFHTTPServerConnectionClose, kCFCompareCaseInsensitive) == kCFCompareEqualTo) ||
((close == NULL) && (version != NULL) &&
CFStringCompare(version, kCFHTTPVersion1_1, kCFCompareCaseInsensitive) != kCFCompareEqualTo))
{
_HttpServerRemoveConnection(connection->_server, connection);
}
if (close != NULL)
CFRelease(close);
if (version != NULL)
CFRelease(version);
}
}
}
}
}
}
void
_HttpConnectionHandleErrorOccurred(HttpConnection* connection, const CFStreamError* error) {
CFArrayRef requests = CFArrayCreateCopy(connection->_alloc, connection->_requests);
CFIndex i, count = CFArrayGetCount(requests);
for (i = 0; i < count; i++) {
CFHTTPMessageRef request = (CFHTTPMessageRef)CFArrayGetValueAtIndex(connection->_requests, i);
CFArrayRef list = (CFArrayRef)CFDictionaryGetValue(connection->_responses, request);
if ((list != NULL) && (connection->_server->_callbacks.errorCallBack != NULL)) {
connection->_server->_callbacks.errorCallBack((_CFHTTPServerRef)connection->_server,
error,
request,
(CFHTTPMessageRef)CFArrayGetValueAtIndex(list, 0),
connection->_server->_ctxt.info);
}
}
CFRelease(requests);
_HttpServerRemoveConnection(connection->_server, connection);
}
void
_HttpConnectionHandleTimeOut(HttpConnection* connection) {
CFStreamError error = {kCFStreamErrorDomainCFHTTPServer, kCFStreamErrorCFHTTPServerTimeout};
_HttpConnectionHandleErrorOccurred(connection, &error);
}
const void*
_ArrayRetainCallBack(CFAllocatorRef allocator, const HttpConnection* connection) {
return _HttpConnectionRetain((HttpConnection*)connection);
}
void
_ArrayReleaseCallBack(CFAllocatorRef allocator, const HttpConnection* connection) {
return _HttpConnectionRelease((HttpConnection*)connection);
}
void
_ReadStreamCallBack(CFReadStreamRef inStream, CFStreamEventType type, HttpConnection* connection) {
assert(inStream == connection->_inStream);
switch (type) {
case kCFStreamEventHasBytesAvailable:
_HttpConnectionHandleHasBytesAvailable(connection);
break;
case kCFStreamEventErrorOccurred:
{
CFStreamError error = CFReadStreamGetError(inStream);
_HttpConnectionHandleErrorOccurred(connection, &error);
}
break;
default:
break;
}
}
void
_WriteStreamCallBack(CFWriteStreamRef outStream, CFStreamEventType type, HttpConnection* connection) {
assert(outStream == connection->_outStream);
switch (type) {
case kCFStreamEventCanAcceptBytes:
_HttpConnectionHandleCanAcceptBytes(connection);
break;
case kCFStreamEventErrorOccurred:
{
CFStreamError error = CFWriteStreamGetError(outStream);
_HttpConnectionHandleErrorOccurred(connection, &error);
}
break;
default:
break;
}
}
void
_TimerCallBack(CFRunLoopTimerRef timer, HttpConnection* connection) {
assert(timer == connection->_timer);
_HttpConnectionHandleTimeOut(connection);
}
void
_HttpServerAddConnection(HttpServer* server, HttpConnection* connection) {
CFArrayAppendValue(server->_connections, connection);
}
void
_HttpServerRemoveConnection(HttpServer* server, HttpConnection* connection) {
CFMutableArrayRef connections = server->_connections;
CFIndex i = CFArrayGetFirstIndexOfValue(connections,
CFRangeMake(0, CFArrayGetCount(connections)),
connection);
if (i != kCFNotFound)
CFArrayRemoveValueAtIndex(connections, i);
}
void
_HttpServerHandleNewConnection(HttpServer* server, CFSocketNativeHandle sock) {
CFAllocatorRef alloc = CFGetAllocator((_CFHTTPServerRef)server);
Boolean accepted = TRUE;
if (server->_callbacks.acceptNewConnectionCallBack) {
uint8_t name[SOCK_MAXADDRLEN];
socklen_t namelen = sizeof(name);
CFDataRef peer = NULL;
if (0 == getpeername(sock, (struct sockaddr *)name, &namelen))
peer = CFDataCreate(alloc, name, namelen);
if (!peer)
accepted = FALSE;
else {
accepted = server->_callbacks.acceptNewConnectionCallBack((_CFHTTPServerRef)server, peer, server->_ctxt.info);
CFRelease(peer);
}
}
if (accepted) {
HttpConnection* connection = _HttpConnectionCreate(alloc, server, sock);
if (connection != NULL) {
_HttpServerAddConnection(server, connection);
_HttpConnectionRelease(connection);
}
else {
CFStreamError error = {kCFStreamErrorDomainCFHTTPServer, kCFStreamErrorCFHTTPServerInternal};
_HttpServerHandleError(server, &error);
}
}
}
void
_HttpServerHandleError(HttpServer* server, const CFStreamError* error) {
if (server->_callbacks.errorCallBack != NULL)
server->_callbacks.errorCallBack((_CFHTTPServerRef)server, error, NULL, NULL, server->_ctxt.info);
}
void
_ServerCallBack(_CFServerRef server, CFSocketNativeHandle sock, const CFStreamError* error, HttpServer* httpServer) {
if (error->error == 0)
_HttpServerHandleNewConnection(httpServer, sock);
else
_HttpServerHandleError(httpServer, error);
}
CFNumberRef
_CFNumberCreateWithString(CFAllocatorRef allocator, CFStringRef string) {
CFIndex i, length = CFStringGetLength(string);
UniChar* buffer = CFAllocatorAllocate(allocator, length * sizeof(buffer[0]), 0);
SInt32 value = 0;
CFStringGetCharacters(string, CFRangeMake(0, length), buffer);
for (i = 0; i < length; i++) {
UniChar c = buffer[i];
if ((c < '0') || (c > '9') || ((value * 10) < value)) {
CFAllocatorDeallocate(allocator, buffer);
return NULL;
}
value *= 10;
value += (c - '0');
}
CFAllocatorDeallocate(allocator, buffer);
return CFNumberCreate(allocator, kCFNumberSInt32Type, &value);
}