CFHTTPServer.c
1 /* 2 * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 /* 24 * CFHTTPServer.c 25 * CFNetwork 26 * 27 * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. 28 * 29 */ 30 31 32 #pragma mark Description 33 /* 34 The http server is comprised of two objects: a HttpServer and a HttpConnection. The 35 HttpServer is primarily responsible for listening and accepting new connections. 36 As individual connections are established, HttpConnections are created in order to 37 maintain that single connection's instance. The HttpServer then maintains the list 38 of these HttpConnection's. 39 40 Individual requests and responses are handled on a per-HttpConnection basis. Although 41 handled on individual HttpConnection's, clients adding responses for a given request 42 do so through the HttpServer's interface. The HttpServer will then pair the response 43 to the request on the proper HttpConnection instance. 44 45 All responses are sent out in the order in which the requests were received. A set 46 of responses can be held up should a response not be available for the head queued 47 item. This can take place since responses are not required to be queued at the 48 moment that a request is received. 49 50 When the response is added to the queue, two items are used. First there is the 51 queue of ordered requests on the connection. This ordered array is maintained in 52 order to send the responses in the correct order. The second part is a dictionary 53 mapping an individual request to its response. A response is comprised of a set of 54 headers (CFHTTPMessageRef) and a stream (CFReadStreamRef) which acts as the body. 55 Every outgoing response has both of these elements. This means that 56 _CFHTTPServerAddResponse creates a stream for the body of the response and then calls 57 _CFHTTPServerAddStreamedResponse. 58 59 Some cheap object model: 60 61 ------------ maintains ---------------- receives --------- 62 |HttpServer|------------@|HttpConnection|----------@|request| 63 ------------ ---------------- --------- 64 | | 65 @ vends | 66 ---------- | 67 |response|----------------- 68 ---------- 69 */ 70 71 #pragma mark - 72 #pragma mark Includes 73 #include "CFServerPriv.h" 74 #include "CFHTTPServerPriv.h" 75 #include "CFNetworkInternal.h" 76 77 #include "CFRuntime.h" 78 #if !defined(__WIN32__) 79 #include <sys/types.h> 80 #ifdef APPORTABLE 81 #if !defined(MAX) 82 #define MAX(A,B) ({ __typeof__(A) __a = (A); __typeof__(B) __b = (B); __a < __b ? __b : __a; }) 83 #endif 84 #include <netinet/in.h> 85 #endif 86 #include <sys/socket.h> 87 #else 88 #include <winsock2.h> 89 #define SOCK_MAXADDRLEN 255 90 #endif 91 92 93 #if 0 94 #pragma mark - 95 #pragma mark Constant Strings 96 #endif 97 98 #ifdef __CONSTANT_CFSTRINGS__ 99 #define _kCFHTTPServerDescribeFormat CFSTR("<HttpServer 0x%x>{server=%@, connections=%@, info=%@}") 100 #define _kCFHTTPServerPtrFormat CFSTR("<0x%x>") 101 #define _kCFHTTPServerContentLengthHeader CFSTR("Content-length") 102 #define _kCFHTTPServerContentLengthFormat CFSTR("%d") 103 #define _kCFHTTPServerConnectionDescribeFormat CFSTR("<_HttpConnection 0x%x>{server=0x%x, timer=%@, inStream=%@, outStream=%@, responses=%@, requests=%@, buffered=%@}") 104 #define _kCFHTTPServerTransferEncodingHeader CFSTR("Transfer-Encoding") 105 #define _kCFHTTPServerTransferEncodingChunked CFSTR("chunked") 106 #define _kCFHTTPServerConnectionHeader CFSTR("Connection") 107 #define _kCFHTTPServerConnectionClose CFSTR("close") 108 #else 109 static CONST_STRING_DECL(_kCFHTTPServerDescribeFormat, "<HttpServer 0x%x>{server=%@, connections=%@, info=%@}") 110 static CONST_STRING_DECL(_kCFHTTPServerPtrFormat, "<0x%x>") 111 static CONST_STRING_DECL(_kCFHTTPServerContentLengthHeader, "Content-length") 112 static CONST_STRING_DECL(_kCFHTTPServerContentLengthFormat, "%d") 113 static CONST_STRING_DECL(_kCFHTTPServerConnectionDescribeFormat, "<_HttpConnection 0x%x>{server=0x%x, timer=%@, inStream=%@, outStream=%@, responses=%@, requests=%@, buffered=%@}") 114 static CONST_STRING_DECL(_kCFHTTPServerTransferEncodingHeader, "Transfer-Encoding") 115 static CONST_STRING_DECL(_kCFHTTPServerTransferEncodingChunked, "chunked") 116 static CONST_STRING_DECL(_kCFHTTPServerConnectionHeader, "Connection") 117 static CONST_STRING_DECL(_kCFHTTPServerConnectionClose, "close") 118 #endif /* __CONSTANT_CFSTRINGS__ */ 119 120 121 #pragma mark - 122 #pragma mark Type Declarations 123 124 typedef struct { 125 CFRuntimeBase _base; // CFRuntimeBase for CF types 126 127 _CFServerRef _server; // Underlying server object. 128 129 CFMutableArrayRef _connections; // All outstanding HttpConnection's 130 131 _CFHTTPServerCallBacks _callbacks; // Callback functions for user 132 _CFHTTPServerContext _ctxt; // User's context for callback 133 } HttpServer; 134 135 136 typedef struct { 137 CFAllocatorRef _alloc; // Allocator used to allocate this 138 UInt32 _rc; // Number of times retained. 139 140 HttpServer* _server; // Reference back to the owning server context. 141 142 CFDataRef _peer; // Peer's address 143 144 CFRunLoopTimerRef _timer; // Timer for controlling timeouts 145 146 CFReadStreamRef _inStream; // Incoming data stream 147 CFWriteStreamRef _outStream; // Outgoing data stream 148 149 CFMutableDictionaryRef _responses; // Responses keyed by their requests 150 CFMutableArrayRef _requests; // Ordered incoming requests 151 152 CFMutableDataRef _bufferedBytes; // Bytes bound for delivery but not yet sent 153 } HttpConnection; 154 155 156 #pragma mark - 157 #pragma mark Static Function Declarations 158 159 // Functions for HttpServer object 160 static void _HttpServerRelease(_CFHTTPServerRef server); 161 static CFStringRef _HttpServerCopyDescription(_CFHTTPServerRef server); 162 163 // Functions for HttpConnection object 164 static HttpConnection* _HttpConnectionCreate(CFAllocatorRef alloc, HttpServer* server, CFSocketNativeHandle s); 165 static HttpConnection* _HttpConnectionRetain(HttpConnection* connection); 166 static void _HttpConnectionRelease(HttpConnection* connection); 167 static CFStringRef _HttpConnectionCopyDescription(HttpConnection* connection); 168 169 // Handlers for HttpConnection object 170 static void _HttpConnectionHandleRequest(HttpConnection* connection); 171 static void _HttpConnectionHandleHasBytesAvailable(HttpConnection* connection); 172 static void _HttpConnectionHandleCanAcceptBytes(HttpConnection* connection); 173 static void _HttpConnectionHandleErrorOccurred(HttpConnection* connection, const CFStreamError* error); 174 static void _HttpConnectionHandleTimeOut(HttpConnection* connection); 175 176 static const void* _ArrayRetainCallBack(CFAllocatorRef allocator, const HttpConnection* connection); 177 static void _ArrayReleaseCallBack(CFAllocatorRef allocator, const HttpConnection* connection); 178 179 180 // CFType callbacks -- call into HttpConnection's handlers 181 static void _ReadStreamCallBack(CFReadStreamRef inStream, CFStreamEventType type, HttpConnection* connection); 182 static void _WriteStreamCallBack(CFWriteStreamRef outStream, CFStreamEventType type, HttpConnection* connection); 183 static void _TimerCallBack(CFRunLoopTimerRef timer, HttpConnection* connection); 184 185 // Functions for manipulating HttpServer's array of HttpConnection's 186 static void _HttpServerAddConnection(HttpServer* server, HttpConnection* connection); 187 static void _HttpServerRemoveConnection(HttpServer* server, HttpConnection* connection); 188 189 // Handlers for HttpServer object 190 static void _HttpServerHandleNewConnection(HttpServer* server, CFSocketNativeHandle sock); 191 static void _HttpServerHandleError(HttpServer* server, const CFStreamError* error); 192 193 // Server callback -- call into HttpServer's handlers 194 static void _ServerCallBack(_CFServerRef server, CFSocketNativeHandle sock, const CFStreamError* error, HttpServer* httpServer); 195 196 // General use function 197 static CFNumberRef _CFNumberCreateWithString(CFAllocatorRef allocator, CFStringRef string); 198 199 200 #if 0 201 #pragma mark - 202 #pragma mark Extern Function Declarations 203 #endif 204 205 extern void _CFSocketStreamCreatePair(CFAllocatorRef alloc, CFStringRef host, UInt32 port, CFSocketNativeHandle s, 206 const CFSocketSignature* sig, CFReadStreamRef* readStream, CFWriteStreamRef* writeStream); 207 208 209 210 211 #pragma mark - 212 #pragma mark Static Variable Definitions 213 214 // A shorter timeout should be used for a more heavily used server. 215 #define kTimeOutInSeconds ((CFTimeInterval)60.0) 216 #define kBufferSize ((CFIndex)8192) 217 218 #define kReadEvents ((CFOptionFlags)(kCFStreamEventHasBytesAvailable | kCFStreamEventErrorOccurred)) 219 #define kWriteEvents ((CFOptionFlags)(kCFStreamEventCanAcceptBytes | kCFStreamEventErrorOccurred)) 220 221 static CFTypeID _HttpServerTypeId = _kCFRuntimeNotATypeID; 222 223 224 #pragma mark - 225 #pragma mark Extern Function Definitions (API) 226 227 228 /* CF_EXPORT */ CFTypeID 229 _CFHTTPServerGetTypeID(void) { 230 231 if (_HttpServerTypeId == _kCFRuntimeNotATypeID) { 232 233 static const CFRuntimeClass HttpServerClass = { 234 0, // version 235 "_CFHTTPServer", // class name 236 NULL, // init 237 NULL, // copy 238 (void(*)(CFTypeRef))_HttpServerRelease, // finalize 239 NULL, // equal 240 NULL, // hash 241 NULL, // copy formatting description 242 243 (CFStringRef(*)(CFTypeRef))_HttpServerCopyDescription // copy debug description 244 }; 245 246 _HttpServerTypeId = _CFRuntimeRegisterClass(&HttpServerClass); 247 } 248 249 return _HttpServerTypeId; 250 } 251 252 253 /* CF_EXPORT */ _CFHTTPServerRef 254 _CFHTTPServerCreate(CFAllocatorRef alloc, const _CFHTTPServerCallBacks* callbacks, _CFHTTPServerContext* context) { 255 256 HttpServer* server = NULL; 257 258 do { 259 _CFServerContext ctxt = { 260 0, 261 NULL, 262 (CFAllocatorRetainCallBack)CFRetain, 263 (CFAllocatorReleaseCallBack)CFRelease, 264 (CFAllocatorCopyDescriptionCallBack)CFCopyDescription 265 }; 266 267 CFArrayCallBacks arrayCallBacks = { 268 0, 269 (CFArrayRetainCallBack)_ArrayRetainCallBack, 270 (CFArrayReleaseCallBack)_ArrayReleaseCallBack, 271 (CFArrayCopyDescriptionCallBack)_HttpConnectionCopyDescription, 272 NULL // Default pointer comparison 273 }; 274 275 CFTypeID id = _CFHTTPServerGetTypeID(); 276 277 // Ask CF to allocate the instance and then return it. 278 if (id != _kCFRuntimeNotATypeID) { 279 server = (HttpServer*)_CFRuntimeCreateInstance(alloc, 280 id, 281 sizeof(HttpServer) - sizeof(CFRuntimeBase), 282 NULL); 283 } 284 285 // Fail if unable to create the server 286 if (server == NULL) 287 break; 288 289 server->_server = NULL; 290 server->_connections = NULL; 291 memset(&server->_callbacks, 0, sizeof(server->_callbacks)); 292 memset(&server->_ctxt, 0, sizeof(server->_ctxt)); 293 294 // Set the info on the callback context 295 ctxt.info = server; 296 297 // Create the server 298 server->_server = _CFServerCreate(alloc, (_CFServerCallBack)_ServerCallBack, &ctxt); 299 300 // Require server in order to create. 301 if (server->_server == NULL) 302 break; 303 304 server->_connections = CFArrayCreateMutable(alloc, 0, &arrayCallBacks); 305 306 // Require the list of outstanding Http connections 307 if (server->_connections == NULL) 308 break; 309 310 // Save the user's callbacks and context. 311 memcpy(&(server->_callbacks), callbacks, sizeof(server->_callbacks)); 312 memcpy(&(server->_ctxt), context, sizeof(server->_ctxt)); 313 314 // If there is info and a retain function, retain the info. 315 if (server->_ctxt.info && server->_ctxt.retain) 316 server->_ctxt.info = (void *)(server->_ctxt.retain(server->_ctxt.info)); 317 318 return (_CFHTTPServerRef)server; 319 320 } while (0); 321 322 // Something failed, so clean up. 323 if (server) { 324 _CFHTTPServerInvalidate((_CFHTTPServerRef)server); 325 CFRelease((_CFHTTPServerRef)server); 326 } 327 328 return NULL; 329 } 330 331 332 /* static */ void 333 _HttpServerRelease(_CFHTTPServerRef server) { 334 335 // Invalidate the server which will release server and outstanding connections. 336 _CFHTTPServerInvalidate(server); 337 } 338 339 340 /* static */ CFStringRef 341 _HttpServerCopyDescription(_CFHTTPServerRef server) { 342 343 CFStringRef info, result, serverDescription = NULL; 344 HttpServer* s = (HttpServer*)server; 345 CFAllocatorRef alloc = CFGetAllocator(server); 346 347 if (s->_server) 348 serverDescription = CFCopyDescription(s->_server); 349 350 // Set the user's context based upon supplied "copyDescription" 351 if (s->_ctxt.copyDescription) 352 info = s->_ctxt.copyDescription(s->_ctxt.info); 353 else 354 info = CFStringCreateWithFormat(alloc, NULL, _kCFHTTPServerPtrFormat, (UInt32)(s->_ctxt.info)); 355 356 // Create the debug string 357 result = CFStringCreateWithFormat(alloc, 358 NULL, 359 _kCFHTTPServerDescribeFormat, 360 (UInt32)s, 361 serverDescription, 362 s->_connections, 363 info); 364 365 if (serverDescription) 366 CFRelease(serverDescription); 367 368 CFRelease(info); 369 370 return result; 371 } 372 373 374 /* CF_EXPORT */ Boolean 375 _CFHTTPServerStart(_CFHTTPServerRef server, CFStringRef name, CFStringRef type, UInt32 port) { 376 377 HttpServer* s = (HttpServer*)server; 378 379 // Nothing special needed for the HTTP server. 380 381 return _CFServerStart(s->_server, name, type, port); 382 } 383 384 385 /* CF_EXPORT */ void 386 _CFHTTPServerInvalidate(_CFHTTPServerRef server) { 387 388 HttpServer* s = (HttpServer*)server; 389 390 // Release the user's context info pointer. 391 if (s->_ctxt.info && s->_ctxt.release) 392 s->_ctxt.release(s->_ctxt.info); 393 394 // Clear out the context, so nothing can be called. 395 memset(&(s->_ctxt), 0, sizeof(s->_ctxt)); 396 397 // Guarantee that there will be no user callbacks. 398 memset(&s->_callbacks, 0, sizeof(s->_callbacks)); 399 400 // Close out any outstanding connections. 401 if (s->_connections) { 402 CFRelease(s->_connections); 403 s->_connections = NULL; 404 } 405 406 // If the server has been created, invalidate it and delete it. 407 if (s->_server) { 408 _CFServerInvalidate(s->_server); 409 CFRelease(s->_server); 410 s->_server = NULL; 411 } 412 } 413 414 415 /* CF_EXPORT */ UInt32 416 _CFHTTPServerGetPort(_CFHTTPServerRef server) { 417 418 return ((HttpServer*)server)->_server ? _CFServerGetPort(((HttpServer*)server)->_server) : 0; 419 } 420 421 422 /* CF_EXPORT */ CFDataRef 423 _CFHTTPServerCopyPeerAddressForRequest(_CFHTTPServerRef server, CFHTTPMessageRef request) { 424 425 CFIndex i, count; 426 HttpServer* s = (HttpServer*)server; 427 428 // Prepare to look for the given request in the connections 429 count = CFArrayGetCount(s->_connections); 430 431 // Start the search 432 for (i = 0; i < count; i++) { 433 434 // **FIXME** This is somewhat incestuous. The server should not be reaching 435 // into the connections. There should really be a HttpConnection method for 436 // adding a response. 437 438 // Pull out the current connection 439 HttpConnection* c = (HttpConnection*)CFArrayGetValueAtIndex(s->_connections, i); 440 441 // Check to see if the connection knows of the request 442 CFIndex j = CFArrayGetFirstIndexOfValue(c->_requests, CFRangeMake(0, CFArrayGetCount(c->_requests)), request); 443 444 // Handle the response if it was found 445 if (j != kCFNotFound) { 446 447 // return the copy that was found 448 return (c->_peer == NULL) ? NULL : CFDataCreateCopy(CFGetAllocator(server), c->_peer); 449 } 450 } 451 452 return NULL; 453 } 454 455 456 /* CF_EXPORT */ void 457 _CFHTTPServerAddResponse(_CFHTTPServerRef server, CFHTTPMessageRef request, CFHTTPMessageRef response) { 458 459 CFDataRef body; 460 UInt8* bytes; 461 CFReadStreamRef stream; 462 CFIndex length; 463 CFStringRef contentLength; 464 465 CFAllocatorRef alloc = CFGetAllocator(server); 466 467 // Make a copy of the response 468 response = CFHTTPMessageCreateCopy(alloc, response); 469 470 // Get the body and its length 471 body = CFHTTPMessageCopyBody(response); 472 473 if (body == NULL) 474 body = CFDataCreate(alloc, NULL, 0); 475 476 length = CFDataGetLength(body); 477 478 // Pull the body off the response since the stream will be used 479 CFHTTPMessageSetBody(response, NULL); 480 481 // Allocate the buffer for the body 482 bytes = (UInt8*)CFAllocatorAllocate(alloc, length, 0); 483 484 // Copy the body into the buffer for streaming 485 memmove(bytes, CFDataGetBytePtr(body), length); 486 487 // Don't need the body anymore 488 CFRelease(body); 489 490 // Create the stream for the body 491 stream = CFReadStreamCreateWithBytesNoCopy(alloc, bytes, length, alloc); 492 493 // Check to see if there is a content length header. 494 contentLength = CFHTTPMessageCopyHeaderFieldValue(response, _kCFHTTPServerContentLengthHeader); 495 496 // If not, add one. 497 if (contentLength == NULL) { 498 499 // Create the header value with the length 500 contentLength = CFStringCreateWithFormat(alloc, NULL, _kCFHTTPServerContentLengthFormat, length); 501 502 // Add the header 503 CFHTTPMessageSetHeaderFieldValue(response, _kCFHTTPServerContentLengthHeader, contentLength); 504 } 505 CFRelease(contentLength); 506 507 // Add the streamed response 508 _CFHTTPServerAddStreamedResponse(server, request, response, stream); 509 510 // No longer needed now that it's in the queue 511 CFRelease(stream); 512 CFRelease(response); 513 } 514 515 516 /* CF_EXPORT */ void 517 _CFHTTPServerAddStreamedResponse(_CFHTTPServerRef server, CFHTTPMessageRef request, CFHTTPMessageRef response, CFReadStreamRef body) { 518 519 CFArrayRef list; 520 CFIndex i, count; 521 522 HttpServer* s = (HttpServer*)server; 523 CFAllocatorRef alloc = CFGetAllocator(server); 524 525 // Things to be put into the response list for a request 526 CFTypeRef objs[] = {NULL, body}; 527 528 // Create a copy 'cause it may need adjustment 529 objs[0] = CFHTTPMessageCreateCopy(alloc, response); 530 531 // Create the response list for the request 532 list = CFArrayCreate(alloc, objs, sizeof(objs) / sizeof(objs[0]), &kCFTypeArrayCallBacks); 533 534 // Prepare to look for the given request in the connections 535 count = CFArrayGetCount(s->_connections); 536 537 // Start the search 538 for (i = 0; i < count; i++) { 539 540 // **FIXME** This is somewhat incestuous. The server should not be reaching 541 // into the connections. There should really be a HttpConnection method for 542 // adding a response. 543 544 // Pull out the current connection 545 HttpConnection* c = (HttpConnection*)CFArrayGetValueAtIndex(s->_connections, i); 546 547 // Check to see if the connection knows of the request 548 CFIndex j = CFArrayGetFirstIndexOfValue(c->_requests, CFRangeMake(0, CFArrayGetCount(c->_requests)), request); 549 550 // Handle the response if it was found 551 if (j != kCFNotFound) { 552 553 // Add the response list to the connection for the given request 554 CFDictionaryAddValue(c->_responses, request, list); 555 556 // If the request was the head of the request queue and the stream can send, pump it. 557 if ((j == 0) && CFWriteStreamCanAcceptBytes(c->_outStream)) 558 _HttpConnectionHandleCanAcceptBytes(c); 559 560 // Everything has been handled 561 break; 562 } 563 } 564 565 // List has been handled, so it's not needed anymore. 566 CFRelease(list); 567 568 CFRelease(objs[0]); 569 } 570 571 572 #pragma mark - 573 #pragma mark Static Function Definitions 574 575 /* static */ HttpConnection* 576 _HttpConnectionCreate(CFAllocatorRef alloc, HttpServer* server, CFSocketNativeHandle s) { 577 578 HttpConnection* connection = NULL; 579 580 do { 581 uint8_t name[SOCK_MAXADDRLEN]; 582 socklen_t namelen = sizeof(name); 583 584 CFRunLoopRef rl = CFRunLoopGetCurrent(); 585 586 CFRunLoopTimerContext timerCtxt = { 587 0, 588 NULL, 589 NULL, 590 NULL, 591 (CFStringRef (*)(const void*))_HttpConnectionCopyDescription 592 }; 593 594 CFStreamClientContext streamCtxt = { 595 0, 596 NULL, 597 NULL, 598 NULL, 599 (CFStringRef (*)(void*))_HttpConnectionCopyDescription 600 }; 601 602 // Allocate the buffer for the connection. 603 connection = CFAllocatorAllocate(alloc, sizeof(connection[0]), 0); 604 605 // Fail if unable to create the connection 606 if (connection == NULL) 607 break; 608 609 memset(connection, 0, sizeof(connection[0])); 610 611 // Save the allocator for deallocating later. 612 connection->_alloc = alloc ? CFRetain(alloc) : NULL; 613 614 // Bump the retain count. 615 _HttpConnectionRetain(connection); 616 617 // Make sure the server is saved for the callback. 618 connection->_server = (HttpServer*)CFRetain((_CFHTTPServerRef)server); 619 620 if (0 == getpeername(s, (struct sockaddr *)name, &namelen)) 621 connection->_peer = CFDataCreate(alloc, name, namelen); 622 623 // Set the info pointer for the contexts to be the connection. 624 timerCtxt.info = connection; 625 streamCtxt.info = connection; 626 627 // Create the timer for detecting dead connections 628 connection->_timer = CFRunLoopTimerCreate(alloc, 629 CFAbsoluteTimeGetCurrent() + kTimeOutInSeconds, 630 kTimeOutInSeconds, 631 0, 632 0, 633 (CFRunLoopTimerCallBack)_TimerCallBack, 634 &timerCtxt); 635 636 // Make sure it succeeded 637 if (connection->_timer == NULL) 638 break; 639 640 // Add the timer to the run loop 641 CFRunLoopAddTimer(rl, connection->_timer, kCFRunLoopCommonModes); 642 643 // Create a pair of streams for performing HTTP. 644 _CFSocketStreamCreatePair(alloc, NULL, 0, s, NULL, &(connection->_inStream), &(connection->_outStream)); 645 646 // Make sure both were created 647 if ((connection->_inStream == NULL) || (connection->_outStream == NULL)) 648 break; 649 650 // Relinquish the socket to the streams 651 CFReadStreamSetProperty(connection->_inStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue); 652 CFWriteStreamSetProperty(connection->_outStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue); 653 654 // Set the client to for each of the streams and for the proper events. 655 CFReadStreamSetClient(connection->_inStream, kReadEvents, (CFReadStreamClientCallBack)_ReadStreamCallBack, &streamCtxt); 656 CFWriteStreamSetClient(connection->_outStream, kWriteEvents, (CFWriteStreamClientCallBack)_WriteStreamCallBack, &streamCtxt); 657 658 // Schedule both on the run loop 659 CFReadStreamScheduleWithRunLoop(connection->_inStream, rl, kCFRunLoopCommonModes); 660 CFWriteStreamScheduleWithRunLoop(connection->_outStream, rl, kCFRunLoopCommonModes); 661 662 // Open up the streams 663 CFReadStreamOpen(connection->_inStream); 664 CFWriteStreamOpen(connection->_outStream); 665 666 // Create the dictionary mapping requests to responses 667 connection->_responses = CFDictionaryCreateMutable(alloc, 668 0, 669 &kCFTypeDictionaryKeyCallBacks, 670 &kCFTypeDictionaryValueCallBacks); 671 672 // Make sure it worked 673 if (connection->_responses == NULL) 674 break; 675 676 // Create the list of all outstanding, incoming requests 677 connection->_requests = CFArrayCreateMutable(alloc, 678 0, 679 &kCFTypeArrayCallBacks); 680 681 // Make sure the list was created 682 if (connection->_requests == NULL) 683 break; 684 685 // Create a buffer for any buffered bytes which will be sent out 686 connection->_bufferedBytes = CFDataCreateMutable(alloc, 0); 687 688 // Make sure there is a buffer 689 if (connection->_bufferedBytes == NULL) 690 break; 691 692 // It's all good 693 return connection; 694 695 } while (0); 696 697 // Something failed, so clean up. 698 if (connection) 699 _HttpConnectionRelease(connection); 700 701 return NULL; 702 } 703 704 705 /* static */ HttpConnection* 706 _HttpConnectionRetain(HttpConnection* connection) { 707 708 // Bump the retain count. 709 connection->_rc++; 710 711 return connection; 712 } 713 714 715 /* static */ void 716 _HttpConnectionRelease(HttpConnection* connection) { 717 718 // Decrease the retain count. 719 connection->_rc--; 720 721 // Destroy the object if not being held. 722 if (connection->_rc == 0) { 723 724 // Hold locally so deallocation can happen and then safely release. 725 CFAllocatorRef alloc = connection->_alloc; 726 727 CFRunLoopRef runLoop = CFRunLoopGetCurrent(); 728 729 if (connection->_server) 730 CFRelease((_CFHTTPServerRef)connection->_server); 731 732 if (connection->_peer) 733 CFRelease(connection->_peer); 734 735 // Check if the read stream exists. 736 if (connection->_inStream) { 737 738 // Unschedule, close, and release it. 739 CFReadStreamSetClient(connection->_inStream, 0, NULL, NULL); 740 CFReadStreamUnscheduleFromRunLoop(connection->_inStream, runLoop, kCFRunLoopCommonModes); 741 CFReadStreamClose(connection->_inStream); 742 CFRelease(connection->_inStream); 743 } 744 745 // Check if the write stream exists. 746 if (connection->_outStream) { 747 748 // Unschedule, close, and release it. 749 CFWriteStreamSetClient(connection->_outStream, 0, NULL, NULL); 750 CFWriteStreamUnscheduleFromRunLoop(connection->_outStream, runLoop, kCFRunLoopCommonModes); 751 CFWriteStreamClose(connection->_outStream); 752 CFRelease(connection->_outStream); 753 } 754 755 // If the timer exists, toss it too. 756 if (connection->_timer != NULL) { 757 CFRunLoopRemoveTimer(runLoop, connection->_timer, kCFRunLoopCommonModes); 758 CFRunLoopTimerInvalidate(connection->_timer); 759 CFRelease(connection->_timer); 760 } 761 762 // Toss the dictionary of requests and responses 763 if (connection->_responses) 764 CFRelease(connection->_responses); 765 766 // Toss the list of incoming requests 767 if (connection->_requests) 768 CFRelease(connection->_requests); 769 770 // Toss the buffered bytes 771 if (connection->_bufferedBytes) 772 CFRelease(connection->_bufferedBytes); 773 774 // Free the memory in use by the connection. 775 CFAllocatorDeallocate(alloc, connection); 776 777 // Release the allocator. 778 if (alloc) 779 CFRelease(alloc); 780 } 781 } 782 783 784 /* static */ CFStringRef 785 _HttpConnectionCopyDescription(HttpConnection* connection) { 786 787 CFStringRef result; 788 789 // Create the debug string 790 result = CFStringCreateWithFormat(connection->_alloc, 791 NULL, 792 _kCFHTTPServerConnectionDescribeFormat, 793 (UInt32)connection, 794 (UInt32)connection->_server, 795 connection->_timer, 796 connection->_inStream, 797 connection->_outStream, 798 connection->_responses, 799 connection->_requests, 800 connection->_bufferedBytes); 801 802 return result; 803 } 804 805 806 /* static */ void 807 _HttpConnectionHandleRequest(HttpConnection* connection) { 808 809 assert(0 != CFArrayGetCount(connection->_requests)); 810 811 // Get the message with which to work (the last one) 812 CFHTTPMessageRef msg = (CFHTTPMessageRef)CFArrayGetValueAtIndex(connection->_requests, 813 CFArrayGetCount(connection->_requests) - 1); 814 815 while (msg) { 816 817 // Use to see if it is a chunked request 818 CFStringRef encoding = CFHTTPMessageCopyHeaderFieldValue(msg, _kCFHTTPServerTransferEncodingHeader); 819 820 // Assume not chunked 821 Boolean chunked = FALSE; 822 823 // If there is encoding, cheaply check for chunked. 824 if (encoding) { 825 chunked = CFStringFindWithOptions(encoding, 826 _kCFHTTPServerTransferEncodingChunked, 827 CFRangeMake(0, CFStringGetLength(encoding)), 828 kCFCompareCaseInsensitive, 829 NULL); 830 CFRelease(encoding); 831 } 832 833 // If it's chunked, bail 'cause the API just isn't ready for 834 // these types of requests yet. 835 if (chunked) { 836 837 // Establish an error 838 CFStreamError error = {kCFStreamErrorDomainHTTP, kCFStreamErrorHTTPParseFailure}; 839 840 // Handle it just like an error. 841 _HttpConnectionHandleErrorOccurred(connection, &error); 842 843 break; 844 } 845 846 // No chunking info so use size 847 else { 848 849 // Assume zero length to start. 850 SInt32 size = 0; 851 852 // Grab the body for testing 853 CFDataRef body = CFHTTPMessageCopyBody(msg); 854 855 // Get the length of the current body on the message 856 CFIndex length = body ? CFDataGetLength(body) : 0; 857 858 // Get the size to see if everything is there. 859 CFStringRef value = CFHTTPMessageCopyHeaderFieldValue(msg, _kCFHTTPServerContentLengthHeader); 860 861 // Need to convert the value if there was a header 862 if (value) { 863 864 // Convert the header value to a CFNumber 865 CFNumberRef num = _CFNumberCreateWithString(connection->_alloc, value); 866 867 // If that succeeded, turn it into the actual size 868 if (num) { 869 870 // Pull out the true expected count of bytes 871 CFNumberGetValue(num, kCFNumberSInt32Type, &size); 872 873 CFRelease(num); 874 } 875 876 // Received a bad content-length header 877 else { 878 879 if (body) CFRelease(body); 880 CFRelease(value); 881 882 // Establish an error 883 CFStreamError error = {kCFStreamErrorDomainHTTP, kCFStreamErrorHTTPParseFailure}; 884 885 // Handle it just like an error. 886 _HttpConnectionHandleErrorOccurred(connection, &error); 887 888 break; 889 } 890 891 CFRelease(value); 892 } 893 894 // If enough bytes haven't arrived, bail out now. 895 if (length < size) { 896 if (body) CFRelease(body); 897 break; 898 } 899 900 // If the message is just right, inform the client of the message 901 // and then exit since this is no more to process. 902 else if (length == size) { 903 904 if (body) CFRelease(body); 905 906 // Inform the client of the incoming request 907 if (connection->_server->_callbacks.didReceiveRequestCallBack != NULL) { 908 CFRetain(msg); 909 connection->_server->_callbacks.didReceiveRequestCallBack((_CFHTTPServerRef)connection->_server, 910 msg, 911 connection->_server->_ctxt.info); 912 CFRelease(msg); 913 } 914 915 break; 916 } 917 918 // There are too many bytes in the body 919 else { 920 921 // Need to make new and truncate, 'cause the current one is too long. 922 CFDataRef newBody = CFDataCreate(connection->_alloc, CFDataGetBytePtr(body), size); 923 924 // Create a new request to capture the leftover bytes. 925 CFHTTPMessageRef newMsg = CFHTTPMessageCreateEmpty(connection->_alloc, TRUE); 926 927 // Set the new body on the first request 928 CFHTTPMessageSetBody(msg, newBody); 929 930 // Toss the new body since it's retained by the request. 931 CFRelease(newBody); 932 933 // Inform the client of the incoming request 934 if (connection->_server->_callbacks.didReceiveRequestCallBack != NULL) { 935 CFRetain(msg); 936 connection->_server->_callbacks.didReceiveRequestCallBack((_CFHTTPServerRef)connection->_server, 937 msg, 938 connection->_server->_ctxt.info); 939 CFRelease(msg); 940 } 941 942 // Move on to the new message to handle it 943 msg = newMsg; 944 945 // Put the new request in the requests list. 946 CFArrayAppendValue(connection->_requests, msg); 947 948 // Drop the retain count now since it's being held by the queue. 949 CFRelease(msg); 950 951 // Add the leftover bytes from the first request to the new one 952 if (!CFHTTPMessageAppendBytes(msg, CFDataGetBytePtr(body) + size, length - size)) { 953 954 // Establish an error 955 CFStreamError error = {kCFStreamErrorDomainHTTP, kCFStreamErrorHTTPParseFailure}; 956 957 // Handle it just like an error. 958 _HttpConnectionHandleErrorOccurred(connection, &error); 959 960 CFRelease(body); 961 962 break; 963 } 964 965 // Don't need the body. 966 CFRelease(body); 967 968 // Check to see if the new message is complete too. 969 if (!CFHTTPMessageIsHeaderComplete(msg)) 970 break; // Not done so bail. 971 972 // There is enough there so inform the client. 973 else { 974 975 // Assume the client is willing to take on the request. 976 Boolean handle = TRUE; 977 978 // Check the client to make sure this new message should be processed. 979 if (connection->_server->_callbacks.acceptNewRequestCallBack) { 980 981 handle = connection->_server->_callbacks.acceptNewRequestCallBack((_CFHTTPServerRef)connection->_server, 982 newMsg, 983 connection->_peer, 984 connection->_server->_ctxt.info); 985 } 986 987 if (!handle) { 988 989 // Remove the connection from the pool 990 _HttpServerRemoveConnection(connection->_server, connection); 991 992 // Bail now because the current connection has been killed 993 break; 994 } 995 } 996 } 997 } 998 } 999 } 1000 1001 1002 /* static */ void 1003 _HttpConnectionHandleHasBytesAvailable(HttpConnection* connection) { 1004 1005 CFIndex bytes; 1006 UInt8 buffer[kBufferSize]; 1007 1008 CFHTTPMessageRef msg; 1009 1010 // Get the count of requests currently known. 1011 CFIndex i = CFArrayGetCount(connection->_requests); 1012 1013 // If there is, grab the last one with which to work 1014 if (i != 0) 1015 msg = (CFHTTPMessageRef)CFArrayGetValueAtIndex(connection->_requests, --i); 1016 1017 else { 1018 1019 // There was no requests, so create a new one with which to work 1020 msg = CFHTTPMessageCreateEmpty(connection->_alloc, TRUE); 1021 CFArrayAppendValue(connection->_requests, msg); 1022 CFRelease(msg); 1023 } 1024 1025 // Try to read bytes off the wire 1026 bytes = CFReadStreamRead(connection->_inStream, buffer, sizeof(buffer)); 1027 1028 // Did it succeed? 1029 if (bytes >= 0) { 1030 1031 Boolean complete = CFHTTPMessageIsHeaderComplete(msg); 1032 1033 // Tickle the timer 1034 CFRunLoopTimerSetNextFireDate(connection->_timer, CFAbsoluteTimeGetCurrent() + kTimeOutInSeconds); 1035 1036 // Attach read bytes to current request 1037 if (!CFHTTPMessageAppendBytes(msg, buffer, bytes)) { 1038 1039 // Establish an error 1040 CFStreamError error = {kCFStreamErrorDomainHTTP, kCFStreamErrorHTTPParseFailure}; 1041 1042 // Handle it just like an error. 1043 _HttpConnectionHandleErrorOccurred(connection, &error); 1044 1045 return; 1046 } 1047 1048 // If the request is complete, handle it as appropriate. 1049 if (CFHTTPMessageIsHeaderComplete(msg)) { 1050 1051 // Assume the client is willing to take on the request. 1052 Boolean handle = TRUE; 1053 1054 // Check the client for sure (each message is checked once when it crosses 1055 // over from incomplete to complete. 1056 if (!complete && connection->_server->_callbacks.acceptNewRequestCallBack) { 1057 1058 handle = connection->_server->_callbacks.acceptNewRequestCallBack((_CFHTTPServerRef)connection->_server, 1059 msg, 1060 connection->_peer, 1061 connection->_server->_ctxt.info); 1062 } 1063 1064 if (handle) 1065 _HttpConnectionHandleRequest(connection); 1066 1067 else { 1068 1069 // Remove the connection from the pool 1070 _HttpServerRemoveConnection(connection->_server, connection); 1071 } 1072 } 1073 } 1074 1075 // Let error conditions come in naturally through the event dispatch. 1076 } 1077 1078 1079 /* static */ void 1080 _HttpConnectionHandleCanAcceptBytes(HttpConnection* connection) { 1081 1082 // How are responses handled (read the "Description" at the top)? 1083 // 1084 // Responses have two parts: a CFHTTPMessageRef containing only headers and a CFReadStreamRef 1085 // which is a stream to the body contents. These responses will be sent in the order in 1086 // which their respective requests were vended. 1087 // 1088 // A local buffer on the connection is used for all writing to the wire. Buffered bytes are 1089 // always sent first. An empty buffer signals the start of a new response. If the buffer is 1090 // empty, the first, queued response's headers are serialized and placed in the buffer. 1091 // 1092 // Bytes in the buffer are sent to the wire. If all bytes in the buffer were written, the 1093 // response's stream is read for bytes. The read bytes are placed into the connection's 1094 // buffer. This buffer will be used for writing when this function is called again. 1095 // 1096 // If the response's stream has been exhausted, that request-response pair is removed from 1097 // the connection's queue, and the buffer is left empty. Since the buffer is empty, the 1098 // next response will be handled when this function is called again. 1099 // 1100 // At the end of each response, the headers are checked for the proper termination of the 1101 // open connection. If a "Connection: close" header exists or if in default mode under 1102 // HTTP version 1.0, the connection will be terminated and dequeued from the server. 1103 1104 // Check to make sure there are queued items. 1105 if (CFArrayGetCount(connection->_requests) != 0) { 1106 1107 // Pull off the request and its related response information 1108 CFHTTPMessageRef request = (CFHTTPMessageRef)CFArrayGetValueAtIndex(connection->_requests, 0); 1109 CFArrayRef list = request ? (CFArrayRef)CFDictionaryGetValue(connection->_responses, request) : NULL; 1110 CFHTTPMessageRef response = list ? (CFHTTPMessageRef)CFArrayGetValueAtIndex(list, 0) : NULL; 1111 CFReadStreamRef stream = list ? (CFReadStreamRef)CFArrayGetValueAtIndex(list, 1) : NULL; 1112 1113 // Only handle if there is a response ready to go 1114 if (list != NULL) { 1115 1116 CFIndex bytesWritten; 1117 1118 // If there are no buffered bytes, need to start the next request. 1119 if (CFDataGetLength(connection->_bufferedBytes) == 0) { 1120 1121 // Serialize if for sending 1122 CFDataRef serialized = CFHTTPMessageCopySerializedMessage(response); 1123 1124 // Get rid of the old one before getting the new 1125 CFRelease(connection->_bufferedBytes); 1126 1127 // Use a mutable copy, because it gets sized down as bytes are sent. 1128 connection->_bufferedBytes = CFDataCreateMutableCopy(connection->_alloc, 0, serialized); 1129 1130 // Release the original. 1131 CFRelease(serialized); 1132 } 1133 1134 // Try writing the entire buffer 1135 bytesWritten = CFWriteStreamWrite(connection->_outStream, 1136 CFDataGetBytePtr(connection->_bufferedBytes), 1137 CFDataGetLength(connection->_bufferedBytes)); 1138 1139 // If successfully wrote, continue on. 1140 if (bytesWritten > 0) { 1141 1142 // Compute the new size of the buffer after the write 1143 CFIndex newSize = CFDataGetLength(connection->_bufferedBytes) - bytesWritten; 1144 1145 // Tickle the timer 1146 CFRunLoopTimerSetNextFireDate(connection->_timer, CFAbsoluteTimeGetCurrent() + kTimeOutInSeconds); 1147 1148 // Move the remaining bytes down in the buffer 1149 memmove(CFDataGetMutableBytePtr(connection->_bufferedBytes), 1150 CFDataGetBytePtr(connection->_bufferedBytes) + bytesWritten, 1151 newSize); 1152 1153 // Resize the buffer to indicate what is left 1154 CFDataSetLength(connection->_bufferedBytes, newSize); 1155 1156 // If nothing left in the buffer, fill the buffer 1157 if (newSize == 0) { 1158 1159 CFIndex bytesRead; 1160 1161 // If the response's stream isn't open yet, open it. 1162 if (CFReadStreamGetStatus(stream) == kCFStreamStatusNotOpen) 1163 CFReadStreamOpen(stream); 1164 1165 // Size the buffer for a full read 1166 CFDataSetLength(connection->_bufferedBytes, kBufferSize); 1167 1168 // Try reading a full buffer into the buffer 1169 bytesRead = CFReadStreamRead(stream, CFDataGetMutableBytePtr(connection->_bufferedBytes), kBufferSize); 1170 1171 // Size the buffer to what ever size was read if successful 1172 if (bytesRead >= 0) 1173 CFDataSetLength(connection->_bufferedBytes, bytesRead); 1174 1175 // Was there an error? 1176 if (bytesRead < 0) { 1177 1178 // Get the error from the read stream 1179 CFStreamError error = CFReadStreamGetError(stream); 1180 1181 // Inform the client of the error. 1182 _HttpConnectionHandleErrorOccurred(connection, &error); 1183 } 1184 1185 // Was this the end of the response's stream? 1186 else if (bytesRead == 0) { 1187 1188 // Get the HTTP version and the connection header from the response. 1189 CFStringRef close = CFHTTPMessageCopyHeaderFieldValue(response, _kCFHTTPServerConnectionHeader); 1190 CFStringRef version = CFHTTPMessageCopyVersion(response); 1191 1192 // If no header, check the original request for one. 1193 if (close == NULL) 1194 close = CFHTTPMessageCopyHeaderFieldValue(request, _kCFHTTPServerConnectionHeader); 1195 1196 // Inform the client of a successful send of the response. 1197 if (connection->_server->_callbacks.didSendResponseCallBack != NULL) { 1198 connection->_server->_callbacks.didSendResponseCallBack((_CFHTTPServerRef)connection->_server, 1199 request, 1200 response, 1201 connection->_server->_ctxt.info); 1202 } 1203 1204 // Remove the request-response pair from the conneciton's queue 1205 CFDictionaryRemoveValue(connection->_responses, request); 1206 CFArrayRemoveValueAtIndex(connection->_requests, 0); 1207 1208 // If there was a header and it said, "close," or if there was no header and HTTP version 1209 // 1.0 is being used, then close the connection and remove it from the server. 1210 if (((close != NULL) && 1211 CFStringCompare(close, _kCFHTTPServerConnectionClose, kCFCompareCaseInsensitive) == kCFCompareEqualTo) || 1212 ((close == NULL) && (version != NULL) && 1213 CFStringCompare(version, kCFHTTPVersion1_1, kCFCompareCaseInsensitive) != kCFCompareEqualTo)) 1214 { 1215 _HttpServerRemoveConnection(connection->_server, connection); 1216 } 1217 if (close != NULL) 1218 CFRelease(close); 1219 1220 if (version != NULL) 1221 CFRelease(version); 1222 } 1223 } 1224 } 1225 } 1226 } 1227 } 1228 1229 1230 /* static */ void 1231 _HttpConnectionHandleErrorOccurred(HttpConnection* connection, const CFStreamError* error) { 1232 1233 CFArrayRef requests = CFArrayCreateCopy(connection->_alloc, connection->_requests); 1234 CFIndex i, count = CFArrayGetCount(requests); 1235 1236 // Error-out each request in the queue 1237 for (i = 0; i < count; i++) { 1238 1239 // Get the request and the response pair 1240 CFHTTPMessageRef request = (CFHTTPMessageRef)CFArrayGetValueAtIndex(connection->_requests, i); 1241 CFArrayRef list = (CFArrayRef)CFDictionaryGetValue(connection->_responses, request); 1242 1243 // If there is a response and there is a client, inform the client of the error. 1244 if ((list != NULL) && (connection->_server->_callbacks.errorCallBack != NULL)) { 1245 connection->_server->_callbacks.errorCallBack((_CFHTTPServerRef)connection->_server, 1246 error, 1247 request, 1248 (CFHTTPMessageRef)CFArrayGetValueAtIndex(list, 0), 1249 connection->_server->_ctxt.info); 1250 } 1251 } 1252 1253 CFRelease(requests); 1254 1255 // Remove the connection from the pool 1256 _HttpServerRemoveConnection(connection->_server, connection); 1257 } 1258 1259 1260 /* static */ void 1261 _HttpConnectionHandleTimeOut(HttpConnection* connection) { 1262 1263 // Establish an error 1264 CFStreamError error = {kCFStreamErrorDomainCFHTTPServer, kCFStreamErrorCFHTTPServerTimeout}; 1265 1266 // Handle it just like an error. 1267 _HttpConnectionHandleErrorOccurred(connection, &error); 1268 } 1269 1270 1271 1272 /* static */ const void* 1273 _ArrayRetainCallBack(CFAllocatorRef allocator, const HttpConnection* connection) { 1274 1275 return _HttpConnectionRetain((HttpConnection*)connection); 1276 } 1277 1278 1279 /* static */ void 1280 _ArrayReleaseCallBack(CFAllocatorRef allocator, const HttpConnection* connection) { 1281 1282 return _HttpConnectionRelease((HttpConnection*)connection); 1283 } 1284 1285 1286 /* static */ void 1287 _ReadStreamCallBack(CFReadStreamRef inStream, CFStreamEventType type, HttpConnection* connection) { 1288 1289 assert(inStream == connection->_inStream); 1290 1291 // Dispatch the event properly. 1292 switch (type) { 1293 1294 case kCFStreamEventHasBytesAvailable: 1295 _HttpConnectionHandleHasBytesAvailable(connection); 1296 break; 1297 1298 case kCFStreamEventErrorOccurred: 1299 { 1300 CFStreamError error = CFReadStreamGetError(inStream); 1301 _HttpConnectionHandleErrorOccurred(connection, &error); 1302 } 1303 break; 1304 1305 default: 1306 break; 1307 } 1308 } 1309 1310 1311 /* static */ void 1312 _WriteStreamCallBack(CFWriteStreamRef outStream, CFStreamEventType type, HttpConnection* connection) { 1313 1314 assert(outStream == connection->_outStream); 1315 1316 // Dispatch the event properly. 1317 switch (type) { 1318 case kCFStreamEventCanAcceptBytes: 1319 _HttpConnectionHandleCanAcceptBytes(connection); 1320 break; 1321 1322 case kCFStreamEventErrorOccurred: 1323 { 1324 CFStreamError error = CFWriteStreamGetError(outStream); 1325 _HttpConnectionHandleErrorOccurred(connection, &error); 1326 } 1327 break; 1328 1329 default: 1330 break; 1331 } 1332 } 1333 1334 1335 /* static */ void 1336 _TimerCallBack(CFRunLoopTimerRef timer, HttpConnection* connection) { 1337 1338 assert(timer == connection->_timer); 1339 1340 // Dispatch the timer event. 1341 _HttpConnectionHandleTimeOut(connection); 1342 } 1343 1344 1345 /* static */ void 1346 _HttpServerAddConnection(HttpServer* server, HttpConnection* connection) { 1347 1348 // Add the given connection to the list 1349 CFArrayAppendValue(server->_connections, connection); 1350 } 1351 1352 1353 /* static */ void 1354 _HttpServerRemoveConnection(HttpServer* server, HttpConnection* connection) { 1355 1356 // Find the given connection in the list of connections 1357 CFMutableArrayRef connections = server->_connections; 1358 CFIndex i = CFArrayGetFirstIndexOfValue(connections, 1359 CFRangeMake(0, CFArrayGetCount(connections)), 1360 connection); 1361 1362 // If it existed, remove it from the list. 1363 if (i != kCFNotFound) 1364 CFArrayRemoveValueAtIndex(connections, i); 1365 } 1366 1367 1368 /* static */ void 1369 _HttpServerHandleNewConnection(HttpServer* server, CFSocketNativeHandle sock) { 1370 1371 CFAllocatorRef alloc = CFGetAllocator((_CFHTTPServerRef)server); 1372 1373 // Assume the server will allow the connection. 1374 Boolean accepted = TRUE; 1375 1376 // Find out if the client cares 1377 if (server->_callbacks.acceptNewConnectionCallBack) { 1378 1379 uint8_t name[SOCK_MAXADDRLEN]; 1380 socklen_t namelen = sizeof(name); 1381 CFDataRef peer = NULL; 1382 1383 // Get the address of the peer. **FIXME** this is less than optimal 1384 // since the peer name is copied again later when the connection is 1385 // created. 1386 if (0 == getpeername(sock, (struct sockaddr *)name, &namelen)) 1387 peer = CFDataCreate(alloc, name, namelen); 1388 1389 // Fail if the peer couldn't be established. 1390 if (!peer) 1391 accepted = FALSE; 1392 1393 else { 1394 1395 // See what the client says. 1396 accepted = server->_callbacks.acceptNewConnectionCallBack((_CFHTTPServerRef)server, peer, server->_ctxt.info); 1397 CFRelease(peer); 1398 } 1399 } 1400 1401 if (accepted) { 1402 1403 // Create a new incoming connection 1404 HttpConnection* connection = _HttpConnectionCreate(alloc, server, sock); 1405 1406 // Add the connection to the server if it created. 1407 if (connection != NULL) { 1408 _HttpServerAddConnection(server, connection); 1409 _HttpConnectionRelease(connection); 1410 } 1411 1412 else { 1413 1414 // Create an error for the bad situation 1415 CFStreamError error = {kCFStreamErrorDomainCFHTTPServer, kCFStreamErrorCFHTTPServerInternal}; 1416 1417 // Handle the error 1418 _HttpServerHandleError(server, &error); 1419 } 1420 } 1421 } 1422 1423 1424 /* static */ void 1425 _HttpServerHandleError(HttpServer* server, const CFStreamError* error) { 1426 1427 // Inform the user of an error. 1428 if (server->_callbacks.errorCallBack != NULL) 1429 server->_callbacks.errorCallBack((_CFHTTPServerRef)server, error, NULL, NULL, server->_ctxt.info); 1430 } 1431 1432 1433 /* static */ void 1434 _ServerCallBack(_CFServerRef server, CFSocketNativeHandle sock, const CFStreamError* error, HttpServer* httpServer) { 1435 1436 if (error->error == 0) 1437 _HttpServerHandleNewConnection(httpServer, sock); 1438 1439 else 1440 _HttpServerHandleError(httpServer, error); 1441 } 1442 1443 1444 /* static */ CFNumberRef 1445 _CFNumberCreateWithString(CFAllocatorRef allocator, CFStringRef string) { 1446 1447 CFIndex i, length = CFStringGetLength(string); 1448 UniChar* buffer = CFAllocatorAllocate(allocator, length * sizeof(buffer[0]), 0); 1449 1450 SInt32 value = 0; 1451 1452 CFStringGetCharacters(string, CFRangeMake(0, length), buffer); 1453 1454 for (i = 0; i < length; i++) { 1455 1456 UniChar c = buffer[i]; 1457 1458 if ((c < '0') || (c > '9') || ((value * 10) < value)) { 1459 CFAllocatorDeallocate(allocator, buffer); 1460 return NULL; 1461 } 1462 1463 value *= 10; 1464 value += (c - '0'); 1465 } 1466 1467 CFAllocatorDeallocate(allocator, buffer); 1468 1469 return CFNumberCreate(allocator, kCFNumberSInt32Type, &value); 1470 } 1471