1/* 2 Copyright (c) 2012-2019, Pierre-Olivier Latour 3 All rights reserved. 4 5 Redistribution and use in source and binary forms, with or without 6 modification, are permitted provided that the following conditions are met: 7 * Redistributions of source code must retain the above copyright 8 notice, this list of conditions and the following disclaimer. 9 * Redistributions in binary form must reproduce the above copyright 10 notice, this list of conditions and the following disclaimer in the 11 documentation and/or other materials provided with the distribution. 12 * The name of Pierre-Olivier Latour may not be used to endorse 13 or promote products derived from this software without specific 14 prior written permission. 15 16 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28#if !__has_feature(objc_arc) 29#error GCDWebServer requires ARC 30#endif 31 32#import <TargetConditionals.h> 33#import <netdb.h> 34#ifdef __GCDWEBSERVER_ENABLE_TESTING__ 35#import <libkern/OSAtomic.h> 36#endif 37 38#import "GCDWebServerPrivate.h" 39 40#define kHeadersReadCapacity (1 * 1024) 41#define kBodyReadCapacity (256 * 1024) 42 43typedef void (^ReadDataCompletionBlock)(BOOL success); 44typedef void (^ReadHeadersCompletionBlock)(NSData* extraData); 45typedef void (^ReadBodyCompletionBlock)(BOOL success); 46 47typedef void (^WriteDataCompletionBlock)(BOOL success); 48typedef void (^WriteHeadersCompletionBlock)(BOOL success); 49typedef void (^WriteBodyCompletionBlock)(BOOL success); 50 51static NSData* _CRLFData = nil; 52static NSData* _CRLFCRLFData = nil; 53static NSData* _continueData = nil; 54static NSData* _lastChunkData = nil; 55static NSString* _digestAuthenticationNonce = nil; 56#ifdef __GCDWEBSERVER_ENABLE_TESTING__ 57static int32_t _connectionCounter = 0; 58#endif 59 60NS_ASSUME_NONNULL_BEGIN 61 62@interface GCDWebServerConnection (Read) 63- (void)readData:(NSMutableData*)data withLength:(NSUInteger)length completionBlock:(ReadDataCompletionBlock)block; 64- (void)readHeaders:(NSMutableData*)headersData withCompletionBlock:(ReadHeadersCompletionBlock)block; 65- (void)readBodyWithRemainingLength:(NSUInteger)length completionBlock:(ReadBodyCompletionBlock)block; 66- (void)readNextBodyChunk:(NSMutableData*)chunkData completionBlock:(ReadBodyCompletionBlock)block; 67@end 68 69@interface GCDWebServerConnection (Write) 70- (void)writeData:(NSData*)data withCompletionBlock:(WriteDataCompletionBlock)block; 71- (void)writeHeadersWithCompletionBlock:(WriteHeadersCompletionBlock)block; 72- (void)writeBodyWithCompletionBlock:(WriteBodyCompletionBlock)block; 73@end 74 75NS_ASSUME_NONNULL_END 76 77@implementation GCDWebServerConnection { 78 CFSocketNativeHandle _socket; 79 BOOL _virtualHEAD; 80 81 CFHTTPMessageRef _requestMessage; 82 GCDWebServerRequest* _request; 83 GCDWebServerHandler* _handler; 84 CFHTTPMessageRef _responseMessage; 85 GCDWebServerResponse* _response; 86 NSInteger _statusCode; 87 88 BOOL _opened; 89#ifdef __GCDWEBSERVER_ENABLE_TESTING__ 90 NSUInteger _connectionIndex; 91 NSString* _requestPath; 92 int _requestFD; 93 NSString* _responsePath; 94 int _responseFD; 95#endif 96} 97 98+ (void)initialize { 99 if (_CRLFData == nil) { 100 _CRLFData = [[NSData alloc] initWithBytes:"\r\n" length:2]; 101 GWS_DCHECK(_CRLFData); 102 } 103 if (_CRLFCRLFData == nil) { 104 _CRLFCRLFData = [[NSData alloc] initWithBytes:"\r\n\r\n" length:4]; 105 GWS_DCHECK(_CRLFCRLFData); 106 } 107 if (_continueData == nil) { 108 CFHTTPMessageRef message = CFHTTPMessageCreateResponse(kCFAllocatorDefault, 100, NULL, kCFHTTPVersion1_1); 109 _continueData = CFBridgingRelease(CFHTTPMessageCopySerializedMessage(message)); 110 CFRelease(message); 111 GWS_DCHECK(_continueData); 112 } 113 if (_lastChunkData == nil) { 114 _lastChunkData = [[NSData alloc] initWithBytes:"0\r\n\r\n" length:5]; 115 } 116 if (_digestAuthenticationNonce == nil) { 117 CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault); 118 _digestAuthenticationNonce = GCDWebServerComputeMD5Digest(@"%@", CFBridgingRelease(CFUUIDCreateString(kCFAllocatorDefault, uuid))); 119 CFRelease(uuid); 120 } 121} 122 123- (BOOL)isUsingIPv6 { 124 const struct sockaddr* localSockAddr = _localAddressData.bytes; 125 return (localSockAddr->sa_family == AF_INET6); 126} 127 128- (void)_initializeResponseHeadersWithStatusCode:(NSInteger)statusCode { 129 _statusCode = statusCode; 130 _responseMessage = CFHTTPMessageCreateResponse(kCFAllocatorDefault, statusCode, NULL, kCFHTTPVersion1_1); 131 CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Connection"), CFSTR("Close")); 132 CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Server"), (__bridge CFStringRef)_server.serverName); 133 CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Date"), (__bridge CFStringRef)GCDWebServerFormatRFC822([NSDate date])); 134} 135 136- (void)_startProcessingRequest { 137 GWS_DCHECK(_responseMessage == NULL); 138 139 GCDWebServerResponse* preflightResponse = [self preflightRequest:_request]; 140 if (preflightResponse) { 141 [self _finishProcessingRequest:preflightResponse]; 142 } else { 143 [self processRequest:_request 144 completion:^(GCDWebServerResponse* processResponse) { 145 [self _finishProcessingRequest:processResponse]; 146 }]; 147 } 148} 149 150// http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html 151- (void)_finishProcessingRequest:(GCDWebServerResponse*)response { 152 GWS_DCHECK(_responseMessage == NULL); 153 BOOL hasBody = NO; 154 155 if (response) { 156 response = [self overrideResponse:response forRequest:_request]; 157 } 158 if (response) { 159 if ([response hasBody]) { 160 [response prepareForReading]; 161 hasBody = !_virtualHEAD; 162 } 163 NSError* error = nil; 164 if (hasBody && ![response performOpen:&error]) { 165 GWS_LOG_ERROR(@"Failed opening response body for socket %i: %@", _socket, error); 166 } else { 167 _response = response; 168 } 169 } 170 171 if (_response) { 172 [self _initializeResponseHeadersWithStatusCode:_response.statusCode]; 173 if (_response.lastModifiedDate) { 174 CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Last-Modified"), (__bridge CFStringRef)GCDWebServerFormatRFC822((NSDate*)_response.lastModifiedDate)); 175 } 176 if (_response.eTag) { 177 CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("ETag"), (__bridge CFStringRef)_response.eTag); 178 } 179 if ((_response.statusCode >= 200) && (_response.statusCode < 300)) { 180 if (_response.cacheControlMaxAge > 0) { 181 CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Cache-Control"), (__bridge CFStringRef)[NSString stringWithFormat:@"max-age=%i, public", (int)_response.cacheControlMaxAge]); 182 } else { 183 CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Cache-Control"), CFSTR("no-cache")); 184 } 185 } 186 if (_response.contentType != nil) { 187 CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Content-Type"), (__bridge CFStringRef)GCDWebServerNormalizeHeaderValue(_response.contentType)); 188 } 189 if (_response.contentLength != NSUIntegerMax) { 190 CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Content-Length"), (__bridge CFStringRef)[NSString stringWithFormat:@"%lu", (unsigned long)_response.contentLength]); 191 } 192 if (_response.usesChunkedTransferEncoding) { 193 CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Transfer-Encoding"), CFSTR("chunked")); 194 } 195 [_response.additionalHeaders enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL* stop) { 196 CFHTTPMessageSetHeaderFieldValue(self->_responseMessage, (__bridge CFStringRef)key, (__bridge CFStringRef)obj); 197 }]; 198 [self writeHeadersWithCompletionBlock:^(BOOL success) { 199 if (success) { 200 if (hasBody) { 201 [self writeBodyWithCompletionBlock:^(BOOL successInner) { 202 [self->_response performClose]; // TODO: There's nothing we can do on failure as headers have already been sent 203 }]; 204 } 205 } else if (hasBody) { 206 [self->_response performClose]; 207 } 208 }]; 209 } else { 210 [self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError]; 211 } 212} 213 214- (void)_readBodyWithLength:(NSUInteger)length initialData:(NSData*)initialData { 215 NSError* error = nil; 216 if (![_request performOpen:&error]) { 217 GWS_LOG_ERROR(@"Failed opening request body for socket %i: %@", _socket, error); 218 [self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError]; 219 return; 220 } 221 222 if (initialData.length) { 223 if (![_request performWriteData:initialData error:&error]) { 224 GWS_LOG_ERROR(@"Failed writing request body on socket %i: %@", _socket, error); 225 if (![_request performClose:&error]) { 226 GWS_LOG_ERROR(@"Failed closing request body for socket %i: %@", _socket, error); 227 } 228 [self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError]; 229 return; 230 } 231 length -= initialData.length; 232 } 233 234 if (length) { 235 [self readBodyWithRemainingLength:length 236 completionBlock:^(BOOL success) { 237 NSError* localError = nil; 238 if ([self->_request performClose:&localError]) { 239 [self _startProcessingRequest]; 240 } else { 241 GWS_LOG_ERROR(@"Failed closing request body for socket %i: %@", self->_socket, error); 242 [self abortRequest:self->_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError]; 243 } 244 }]; 245 } else { 246 if ([_request performClose:&error]) { 247 [self _startProcessingRequest]; 248 } else { 249 GWS_LOG_ERROR(@"Failed closing request body for socket %i: %@", _socket, error); 250 [self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError]; 251 } 252 } 253} 254 255- (void)_readChunkedBodyWithInitialData:(NSData*)initialData { 256 NSError* error = nil; 257 if (![_request performOpen:&error]) { 258 GWS_LOG_ERROR(@"Failed opening request body for socket %i: %@", _socket, error); 259 [self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError]; 260 return; 261 } 262 263 NSMutableData* chunkData = [[NSMutableData alloc] initWithData:initialData]; 264 [self readNextBodyChunk:chunkData 265 completionBlock:^(BOOL success) { 266 NSError* localError = nil; 267 if ([self->_request performClose:&localError]) { 268 [self _startProcessingRequest]; 269 } else { 270 GWS_LOG_ERROR(@"Failed closing request body for socket %i: %@", self->_socket, error); 271 [self abortRequest:self->_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError]; 272 } 273 }]; 274} 275 276- (void)_readRequestHeaders { 277 _requestMessage = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, true); 278 NSMutableData* headersData = [[NSMutableData alloc] initWithCapacity:kHeadersReadCapacity]; 279 [self readHeaders:headersData 280 withCompletionBlock:^(NSData* extraData) { 281 if (extraData) { 282 NSString* requestMethod = CFBridgingRelease(CFHTTPMessageCopyRequestMethod(self->_requestMessage)); // Method verbs are case-sensitive and uppercase 283 if (self->_server.shouldAutomaticallyMapHEADToGET && [requestMethod isEqualToString:@"HEAD"]) { 284 requestMethod = @"GET"; 285 self->_virtualHEAD = YES; 286 } 287 NSDictionary* requestHeaders = CFBridgingRelease(CFHTTPMessageCopyAllHeaderFields(self->_requestMessage)); // Header names are case-insensitive but CFHTTPMessageCopyAllHeaderFields() will standardize the common ones 288 NSURL* requestURL = CFBridgingRelease(CFHTTPMessageCopyRequestURL(self->_requestMessage)); 289 if (requestURL) { 290 requestURL = [self rewriteRequestURL:requestURL withMethod:requestMethod headers:requestHeaders]; 291 GWS_DCHECK(requestURL); 292 } 293 NSString* urlPath = requestURL ? CFBridgingRelease(CFURLCopyPath((CFURLRef)requestURL)) : nil; // Don't use -[NSURL path] which strips the ending slash 294 if (urlPath == nil) { 295 urlPath = @"/"; // CFURLCopyPath() returns NULL for a relative URL with path "//" contrary to -[NSURL path] which returns "/" 296 } 297 NSString* requestPath = urlPath ? GCDWebServerUnescapeURLString(urlPath) : nil; 298 NSString* queryString = requestURL ? CFBridgingRelease(CFURLCopyQueryString((CFURLRef)requestURL, NULL)) : nil; // Don't use -[NSURL query] to make sure query is not unescaped; 299 NSDictionary* requestQuery = queryString ? GCDWebServerParseURLEncodedForm(queryString) : @{}; 300 if (requestMethod && requestURL && requestHeaders && requestPath && requestQuery) { 301 for (self->_handler in self->_server.handlers) { 302 self->_request = self->_handler.matchBlock(requestMethod, requestURL, requestHeaders, requestPath, requestQuery); 303 if (self->_request) { 304 break; 305 } 306 } 307 if (self->_request) { 308 self->_request.localAddressData = self.localAddressData; 309 self->_request.remoteAddressData = self.remoteAddressData; 310 if ([self->_request hasBody]) { 311 [self->_request prepareForWriting]; 312 if (self->_request.usesChunkedTransferEncoding || (extraData.length <= self->_request.contentLength)) { 313 NSString* expectHeader = [requestHeaders objectForKey:@"Expect"]; 314 if (expectHeader) { 315 if ([expectHeader caseInsensitiveCompare:@"100-continue"] == NSOrderedSame) { // TODO: Actually validate request before continuing 316 [self writeData:_continueData 317 withCompletionBlock:^(BOOL success) { 318 if (success) { 319 if (self->_request.usesChunkedTransferEncoding) { 320 [self _readChunkedBodyWithInitialData:extraData]; 321 } else { 322 [self _readBodyWithLength:self->_request.contentLength initialData:extraData]; 323 } 324 } 325 }]; 326 } else { 327 GWS_LOG_ERROR(@"Unsupported 'Expect' / 'Content-Length' header combination on socket %i", self->_socket); 328 [self abortRequest:self->_request withStatusCode:kGCDWebServerHTTPStatusCode_ExpectationFailed]; 329 } 330 } else { 331 if (self->_request.usesChunkedTransferEncoding) { 332 [self _readChunkedBodyWithInitialData:extraData]; 333 } else { 334 [self _readBodyWithLength:self->_request.contentLength initialData:extraData]; 335 } 336 } 337 } else { 338 GWS_LOG_ERROR(@"Unexpected 'Content-Length' header value on socket %i", self->_socket); 339 [self abortRequest:self->_request withStatusCode:kGCDWebServerHTTPStatusCode_BadRequest]; 340 } 341 } else { 342 [self _startProcessingRequest]; 343 } 344 } else { 345 self->_request = [[GCDWebServerRequest alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:requestPath query:requestQuery]; 346 GWS_DCHECK(self->_request); 347 [self abortRequest:self->_request withStatusCode:kGCDWebServerHTTPStatusCode_NotImplemented]; 348 } 349 } else { 350 [self abortRequest:nil withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError]; 351 GWS_DNOT_REACHED(); 352 } 353 } else { 354 [self abortRequest:nil withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError]; 355 } 356 }]; 357} 358 359- (instancetype)initWithServer:(GCDWebServer*)server localAddress:(NSData*)localAddress remoteAddress:(NSData*)remoteAddress socket:(CFSocketNativeHandle)socket { 360 if ((self = [super init])) { 361 _server = server; 362 _localAddressData = localAddress; 363 _remoteAddressData = remoteAddress; 364 _socket = socket; 365 GWS_LOG_DEBUG(@"Did open connection on socket %i", _socket); 366 367 [_server willStartConnection:self]; 368 369 if (![self open]) { 370 close(_socket); 371 return nil; 372 } 373 _opened = YES; 374 375 [self _readRequestHeaders]; 376 } 377 return self; 378} 379 380- (NSString*)localAddressString { 381 return GCDWebServerStringFromSockAddr(_localAddressData.bytes, YES); 382} 383 384- (NSString*)remoteAddressString { 385 return GCDWebServerStringFromSockAddr(_remoteAddressData.bytes, YES); 386} 387 388- (void)dealloc { 389 int result = close(_socket); 390 if (result != 0) { 391 GWS_LOG_ERROR(@"Failed closing socket %i for connection: %s (%i)", _socket, strerror(errno), errno); 392 } else { 393 GWS_LOG_DEBUG(@"Did close connection on socket %i", _socket); 394 } 395 396 if (_opened) { 397 [self close]; 398 } 399 400 [_server didEndConnection:self]; 401 402 if (_requestMessage) { 403 CFRelease(_requestMessage); 404 } 405 406 if (_responseMessage) { 407 CFRelease(_responseMessage); 408 } 409} 410 411@end 412 413@implementation GCDWebServerConnection (Read) 414 415- (void)readData:(NSMutableData*)data withLength:(NSUInteger)length completionBlock:(ReadDataCompletionBlock)block { 416 dispatch_read(_socket, length, dispatch_get_global_queue(_server.dispatchQueuePriority, 0), ^(dispatch_data_t buffer, int error) { 417 @autoreleasepool { 418 if (error == 0) { 419 size_t size = dispatch_data_get_size(buffer); 420 if (size > 0) { 421 NSUInteger originalLength = data.length; 422 dispatch_data_apply(buffer, ^bool(dispatch_data_t region, size_t chunkOffset, const void* chunkBytes, size_t chunkSize) { 423 [data appendBytes:chunkBytes length:chunkSize]; 424 return true; 425 }); 426 [self didReadBytes:((char*)data.bytes + originalLength) length:(data.length - originalLength)]; 427 block(YES); 428 } else { 429 if (self->_totalBytesRead > 0) { 430 GWS_LOG_ERROR(@"No more data available on socket %i", self->_socket); 431 } else { 432 GWS_LOG_WARNING(@"No data received from socket %i", self->_socket); 433 } 434 block(NO); 435 } 436 } else { 437 GWS_LOG_ERROR(@"Error while reading from socket %i: %s (%i)", self->_socket, strerror(error), error); 438 block(NO); 439 } 440 } 441 }); 442} 443 444- (void)readHeaders:(NSMutableData*)headersData withCompletionBlock:(ReadHeadersCompletionBlock)block { 445 GWS_DCHECK(_requestMessage); 446 [self readData:headersData 447 withLength:NSUIntegerMax 448 completionBlock:^(BOOL success) { 449 if (success) { 450 NSRange range = [headersData rangeOfData:_CRLFCRLFData options:0 range:NSMakeRange(0, headersData.length)]; 451 if (range.location == NSNotFound) { 452 [self readHeaders:headersData withCompletionBlock:block]; 453 } else { 454 NSUInteger length = range.location + range.length; 455 if (CFHTTPMessageAppendBytes(self->_requestMessage, headersData.bytes, length)) { 456 if (CFHTTPMessageIsHeaderComplete(self->_requestMessage)) { 457 block([headersData subdataWithRange:NSMakeRange(length, headersData.length - length)]); 458 } else { 459 GWS_LOG_ERROR(@"Failed parsing request headers from socket %i", self->_socket); 460 block(nil); 461 } 462 } else { 463 GWS_LOG_ERROR(@"Failed appending request headers data from socket %i", self->_socket); 464 block(nil); 465 } 466 } 467 } else { 468 block(nil); 469 } 470 }]; 471} 472 473- (void)readBodyWithRemainingLength:(NSUInteger)length completionBlock:(ReadBodyCompletionBlock)block { 474 GWS_DCHECK([_request hasBody] && ![_request usesChunkedTransferEncoding]); 475 NSMutableData* bodyData = [[NSMutableData alloc] initWithCapacity:kBodyReadCapacity]; 476 [self readData:bodyData 477 withLength:length 478 completionBlock:^(BOOL success) { 479 if (success) { 480 if (bodyData.length <= length) { 481 NSError* error = nil; 482 if ([self->_request performWriteData:bodyData error:&error]) { 483 NSUInteger remainingLength = length - bodyData.length; 484 if (remainingLength) { 485 [self readBodyWithRemainingLength:remainingLength completionBlock:block]; 486 } else { 487 block(YES); 488 } 489 } else { 490 GWS_LOG_ERROR(@"Failed writing request body on socket %i: %@", self->_socket, error); 491 block(NO); 492 } 493 } else { 494 GWS_LOG_ERROR(@"Unexpected extra content reading request body on socket %i", self->_socket); 495 block(NO); 496 GWS_DNOT_REACHED(); 497 } 498 } else { 499 block(NO); 500 } 501 }]; 502} 503 504static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) { 505 char buffer[size + 1]; 506 bcopy(bytes, buffer, size); 507 buffer[size] = 0; 508 char* end = NULL; 509 long result = strtol(buffer, &end, 16); 510 return ((end != NULL) && (*end == 0) && (result >= 0) ? result : NSNotFound); 511} 512 513- (void)readNextBodyChunk:(NSMutableData*)chunkData completionBlock:(ReadBodyCompletionBlock)block { 514 GWS_DCHECK([_request hasBody] && [_request usesChunkedTransferEncoding]); 515 516 while (1) { 517 NSRange range = [chunkData rangeOfData:_CRLFData options:0 range:NSMakeRange(0, chunkData.length)]; 518 if (range.location == NSNotFound) { 519 break; 520 } 521 NSRange extensionRange = [chunkData rangeOfData:[NSData dataWithBytes:";" length:1] options:0 range:NSMakeRange(0, range.location)]; // Ignore chunk extensions 522 NSUInteger length = _ScanHexNumber((char*)chunkData.bytes, extensionRange.location != NSNotFound ? extensionRange.location : range.location); 523 if (length != NSNotFound) { 524 if (length) { 525 if (chunkData.length < range.location + range.length + length + 2) { 526 break; 527 } 528 const char* ptr = (char*)chunkData.bytes + range.location + range.length + length; 529 if ((*ptr == '\r') && (*(ptr + 1) == '\n')) { 530 NSError* error = nil; 531 if ([_request performWriteData:[chunkData subdataWithRange:NSMakeRange(range.location + range.length, length)] error:&error]) { 532 [chunkData replaceBytesInRange:NSMakeRange(0, range.location + range.length + length + 2) withBytes:NULL length:0]; 533 } else { 534 GWS_LOG_ERROR(@"Failed writing request body on socket %i: %@", _socket, error); 535 block(NO); 536 return; 537 } 538 } else { 539 GWS_LOG_ERROR(@"Missing terminating CRLF sequence for chunk reading request body on socket %i", _socket); 540 block(NO); 541 return; 542 } 543 } else { 544 NSRange trailerRange = [chunkData rangeOfData:_CRLFCRLFData options:0 range:NSMakeRange(range.location, chunkData.length - range.location)]; // Ignore trailers 545 if (trailerRange.location != NSNotFound) { 546 block(YES); 547 return; 548 } 549 } 550 } else { 551 GWS_LOG_ERROR(@"Invalid chunk length reading request body on socket %i", _socket); 552 block(NO); 553 return; 554 } 555 } 556 557 [self readData:chunkData 558 withLength:NSUIntegerMax 559 completionBlock:^(BOOL success) { 560 if (success) { 561 [self readNextBodyChunk:chunkData completionBlock:block]; 562 } else { 563 block(NO); 564 } 565 }]; 566} 567 568@end 569 570@implementation GCDWebServerConnection (Write) 571 572- (void)writeData:(NSData*)data withCompletionBlock:(WriteDataCompletionBlock)block { 573 dispatch_data_t buffer = dispatch_data_create(data.bytes, data.length, dispatch_get_global_queue(_server.dispatchQueuePriority, 0), ^{ 574 [data self]; // Keeps ARC from releasing data too early 575 }); 576 dispatch_write(_socket, buffer, dispatch_get_global_queue(_server.dispatchQueuePriority, 0), ^(dispatch_data_t remainingData, int error) { 577 @autoreleasepool { 578 if (error == 0) { 579 GWS_DCHECK(remainingData == NULL); 580 [self didWriteBytes:data.bytes length:data.length]; 581 block(YES); 582 } else { 583 GWS_LOG_ERROR(@"Error while writing to socket %i: %s (%i)", self->_socket, strerror(error), error); 584 block(NO); 585 } 586 } 587 }); 588#if !OS_OBJECT_USE_OBJC_RETAIN_RELEASE 589 dispatch_release(buffer); 590#endif 591} 592 593- (void)writeHeadersWithCompletionBlock:(WriteHeadersCompletionBlock)block { 594 GWS_DCHECK(_responseMessage); 595 CFDataRef data = CFHTTPMessageCopySerializedMessage(_responseMessage); 596 [self writeData:(__bridge NSData*)data withCompletionBlock:block]; 597 CFRelease(data); 598} 599 600- (void)writeBodyWithCompletionBlock:(WriteBodyCompletionBlock)block { 601 GWS_DCHECK([_response hasBody]); 602 [_response performReadDataWithCompletion:^(NSData* data, NSError* error) { 603 if (data) { 604 if (data.length) { 605 if (self->_response.usesChunkedTransferEncoding) { 606 const char* hexString = [[NSString stringWithFormat:@"%lx", (unsigned long)data.length] UTF8String]; 607 size_t hexLength = strlen(hexString); 608 NSData* chunk = [NSMutableData dataWithLength:(hexLength + 2 + data.length + 2)]; 609 if (chunk == nil) { 610 GWS_LOG_ERROR(@"Failed allocating memory for response body chunk for socket %i: %@", self->_socket, error); 611 block(NO); 612 return; 613 } 614 char* ptr = (char*)[(NSMutableData*)chunk mutableBytes]; 615 bcopy(hexString, ptr, hexLength); 616 ptr += hexLength; 617 *ptr++ = '\r'; 618 *ptr++ = '\n'; 619 bcopy(data.bytes, ptr, data.length); 620 ptr += data.length; 621 *ptr++ = '\r'; 622 *ptr = '\n'; 623 data = chunk; 624 } 625 [self writeData:data 626 withCompletionBlock:^(BOOL success) { 627 if (success) { 628 [self writeBodyWithCompletionBlock:block]; 629 } else { 630 block(NO); 631 } 632 }]; 633 } else { 634 if (self->_response.usesChunkedTransferEncoding) { 635 [self writeData:_lastChunkData 636 withCompletionBlock:^(BOOL success) { 637 block(success); 638 }]; 639 } else { 640 block(YES); 641 } 642 } 643 } else { 644 GWS_LOG_ERROR(@"Failed reading response body for socket %i: %@", self->_socket, error); 645 block(NO); 646 } 647 }]; 648} 649 650@end 651 652@implementation GCDWebServerConnection (Subclassing) 653 654- (BOOL)open { 655#ifdef __GCDWEBSERVER_ENABLE_TESTING__ 656 if (_server.recordingEnabled) { 657#pragma clang diagnostic push 658#pragma clang diagnostic ignored "-Wdeprecated-declarations" 659 _connectionIndex = OSAtomicIncrement32(&_connectionCounter); 660#pragma clang diagnostic pop 661 662 _requestPath = [NSTemporaryDirectory() stringByAppendingPathComponent:[[NSProcessInfo processInfo] globallyUniqueString]]; 663 _requestFD = open([_requestPath fileSystemRepresentation], O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); 664 GWS_DCHECK(_requestFD > 0); 665 666 _responsePath = [NSTemporaryDirectory() stringByAppendingPathComponent:[[NSProcessInfo processInfo] globallyUniqueString]]; 667 _responseFD = open([_responsePath fileSystemRepresentation], O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); 668 GWS_DCHECK(_responseFD > 0); 669 } 670#endif 671 672 return YES; 673} 674 675- (void)didReadBytes:(const void*)bytes length:(NSUInteger)length { 676 GWS_LOG_DEBUG(@"Connection received %lu bytes on socket %i", (unsigned long)length, _socket); 677 _totalBytesRead += length; 678 679#ifdef __GCDWEBSERVER_ENABLE_TESTING__ 680 if ((_requestFD > 0) && (write(_requestFD, bytes, length) != (ssize_t)length)) { 681 GWS_LOG_ERROR(@"Failed recording request data: %s (%i)", strerror(errno), errno); 682 close(_requestFD); 683 _requestFD = 0; 684 } 685#endif 686} 687 688- (void)didWriteBytes:(const void*)bytes length:(NSUInteger)length { 689 GWS_LOG_DEBUG(@"Connection sent %lu bytes on socket %i", (unsigned long)length, _socket); 690 _totalBytesWritten += length; 691 692#ifdef __GCDWEBSERVER_ENABLE_TESTING__ 693 if ((_responseFD > 0) && (write(_responseFD, bytes, length) != (ssize_t)length)) { 694 GWS_LOG_ERROR(@"Failed recording response data: %s (%i)", strerror(errno), errno); 695 close(_responseFD); 696 _responseFD = 0; 697 } 698#endif 699} 700 701- (NSURL*)rewriteRequestURL:(NSURL*)url withMethod:(NSString*)method headers:(NSDictionary<NSString*, NSString*>*)headers { 702 return url; 703} 704 705// https://tools.ietf.org/html/rfc2617 706- (GCDWebServerResponse*)preflightRequest:(GCDWebServerRequest*)request { 707 GWS_LOG_DEBUG(@"Connection on socket %i preflighting request \"%@ %@\" with %lu bytes body", _socket, _virtualHEAD ? @"HEAD" : _request.method, _request.path, (unsigned long)_totalBytesRead); 708 GCDWebServerResponse* response = nil; 709 if (_server.authenticationBasicAccounts) { 710 __block BOOL authenticated = NO; 711 NSString* authorizationHeader = [request.headers objectForKey:@"Authorization"]; 712 if ([authorizationHeader hasPrefix:@"Basic "]) { 713 NSString* basicAccount = [authorizationHeader substringFromIndex:6]; 714 [_server.authenticationBasicAccounts enumerateKeysAndObjectsUsingBlock:^(NSString* username, NSString* digest, BOOL* stop) { 715 if ([basicAccount isEqualToString:digest]) { 716 authenticated = YES; 717 *stop = YES; 718 } 719 }]; 720 } 721 if (!authenticated) { 722 response = [GCDWebServerResponse responseWithStatusCode:kGCDWebServerHTTPStatusCode_Unauthorized]; 723 [response setValue:[NSString stringWithFormat:@"Basic realm=\"%@\"", _server.authenticationRealm] forAdditionalHeader:@"WWW-Authenticate"]; 724 } 725 } else if (_server.authenticationDigestAccounts) { 726 BOOL authenticated = NO; 727 BOOL isStaled = NO; 728 NSString* authorizationHeader = [request.headers objectForKey:@"Authorization"]; 729 if ([authorizationHeader hasPrefix:@"Digest "]) { 730 NSString* realm = GCDWebServerExtractHeaderValueParameter(authorizationHeader, @"realm"); 731 if (realm && [_server.authenticationRealm isEqualToString:realm]) { 732 NSString* nonce = GCDWebServerExtractHeaderValueParameter(authorizationHeader, @"nonce"); 733 if ([nonce isEqualToString:_digestAuthenticationNonce]) { 734 NSString* username = GCDWebServerExtractHeaderValueParameter(authorizationHeader, @"username"); 735 NSString* uri = GCDWebServerExtractHeaderValueParameter(authorizationHeader, @"uri"); 736 NSString* actualResponse = GCDWebServerExtractHeaderValueParameter(authorizationHeader, @"response"); 737 NSString* ha1 = [_server.authenticationDigestAccounts objectForKey:username]; 738 NSString* ha2 = GCDWebServerComputeMD5Digest(@"%@:%@", request.method, uri); // We cannot use "request.path" as the query string is required 739 NSString* expectedResponse = GCDWebServerComputeMD5Digest(@"%@:%@:%@", ha1, _digestAuthenticationNonce, ha2); 740 if ([actualResponse isEqualToString:expectedResponse]) { 741 authenticated = YES; 742 } 743 } else if (nonce.length) { 744 isStaled = YES; 745 } 746 } 747 } 748 if (!authenticated) { 749 response = [GCDWebServerResponse responseWithStatusCode:kGCDWebServerHTTPStatusCode_Unauthorized]; 750 [response setValue:[NSString stringWithFormat:@"Digest realm=\"%@\", nonce=\"%@\"%@", _server.authenticationRealm, _digestAuthenticationNonce, isStaled ? @", stale=TRUE" : @""] forAdditionalHeader:@"WWW-Authenticate"]; // TODO: Support Quality of Protection ("qop") 751 } 752 } 753 return response; 754} 755 756- (void)processRequest:(GCDWebServerRequest*)request completion:(GCDWebServerCompletionBlock)completion { 757 GWS_LOG_DEBUG(@"Connection on socket %i processing request \"%@ %@\" with %lu bytes body", _socket, _virtualHEAD ? @"HEAD" : _request.method, _request.path, (unsigned long)_totalBytesRead); 758 _handler.asyncProcessBlock(request, [completion copy]); 759} 760 761// http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.25 762// http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.26 763static inline BOOL _CompareResources(NSString* responseETag, NSString* requestETag, NSDate* responseLastModified, NSDate* requestLastModified) { 764 if (requestLastModified && responseLastModified) { 765 if ([responseLastModified compare:requestLastModified] != NSOrderedDescending) { 766 return YES; 767 } 768 } 769 if (requestETag && responseETag) { // Per the specs "If-None-Match" must be checked after "If-Modified-Since" 770 if ([requestETag isEqualToString:@"*"]) { 771 return YES; 772 } 773 if ([responseETag isEqualToString:requestETag]) { 774 return YES; 775 } 776 } 777 return NO; 778} 779 780- (GCDWebServerResponse*)overrideResponse:(GCDWebServerResponse*)response forRequest:(GCDWebServerRequest*)request { 781 if ((response.statusCode >= 200) && (response.statusCode < 300) && _CompareResources(response.eTag, request.ifNoneMatch, response.lastModifiedDate, request.ifModifiedSince)) { 782 NSInteger code = [request.method isEqualToString:@"HEAD"] || [request.method isEqualToString:@"GET"] ? kGCDWebServerHTTPStatusCode_NotModified : kGCDWebServerHTTPStatusCode_PreconditionFailed; 783 GCDWebServerResponse* newResponse = [GCDWebServerResponse responseWithStatusCode:code]; 784 newResponse.cacheControlMaxAge = response.cacheControlMaxAge; 785 newResponse.lastModifiedDate = response.lastModifiedDate; 786 newResponse.eTag = response.eTag; 787 GWS_DCHECK(newResponse); 788 return newResponse; 789 } 790 return response; 791} 792 793- (void)abortRequest:(GCDWebServerRequest*)request withStatusCode:(NSInteger)statusCode { 794 GWS_DCHECK(_responseMessage == NULL); 795 GWS_DCHECK((statusCode >= 400) && (statusCode < 600)); 796 [self _initializeResponseHeadersWithStatusCode:statusCode]; 797 [self writeHeadersWithCompletionBlock:^(BOOL success) { 798 ; // Nothing more to do 799 }]; 800 GWS_LOG_DEBUG(@"Connection aborted with status code %i on socket %i", (int)statusCode, _socket); 801} 802 803- (void)close { 804#ifdef __GCDWEBSERVER_ENABLE_TESTING__ 805 if (_requestPath) { 806 BOOL success = NO; 807 NSError* error = nil; 808 if (_requestFD > 0) { 809 close(_requestFD); 810 NSString* name = [NSString stringWithFormat:@"%03lu-%@.request", (unsigned long)_connectionIndex, _virtualHEAD ? @"HEAD" : _request.method]; 811 success = [[NSFileManager defaultManager] moveItemAtPath:_requestPath toPath:[[[NSFileManager defaultManager] currentDirectoryPath] stringByAppendingPathComponent:name] error:&error]; 812 } 813 if (!success) { 814 GWS_LOG_ERROR(@"Failed saving recorded request: %@", error); 815 GWS_DNOT_REACHED(); 816 } 817 unlink([_requestPath fileSystemRepresentation]); 818 } 819 820 if (_responsePath) { 821 BOOL success = NO; 822 NSError* error = nil; 823 if (_responseFD > 0) { 824 close(_responseFD); 825 NSString* name = [NSString stringWithFormat:@"%03lu-%i.response", (unsigned long)_connectionIndex, (int)_statusCode]; 826 success = [[NSFileManager defaultManager] moveItemAtPath:_responsePath toPath:[[[NSFileManager defaultManager] currentDirectoryPath] stringByAppendingPathComponent:name] error:&error]; 827 } 828 if (!success) { 829 GWS_LOG_ERROR(@"Failed saving recorded response: %@", error); 830 GWS_DNOT_REACHED(); 831 } 832 unlink([_responsePath fileSystemRepresentation]); 833 } 834#endif 835 836 if (_request) { 837 GWS_LOG_VERBOSE(@"[%@] %@ %i \"%@ %@\" (%lu | %lu)", self.localAddressString, self.remoteAddressString, (int)_statusCode, _virtualHEAD ? @"HEAD" : _request.method, _request.path, (unsigned long)_totalBytesRead, (unsigned long)_totalBytesWritten); 838 } else { 839 GWS_LOG_VERBOSE(@"[%@] %@ %i \"(invalid request)\" (%lu | %lu)", self.localAddressString, self.remoteAddressString, (int)_statusCode, (unsigned long)_totalBytesRead, (unsigned long)_totalBytesWritten); 840 } 841} 842 843@end 844