|
@@ -57,14 +57,25 @@ static NSString* _digestAuthenticationNonce = nil;
|
|
static int32_t _connectionCounter = 0;
|
|
static int32_t _connectionCounter = 0;
|
|
#endif
|
|
#endif
|
|
|
|
|
|
-@interface GCDWebServerConnection () {
|
|
|
|
-@private
|
|
|
|
- GCDWebServer* _server;
|
|
|
|
- NSData* _localAddress;
|
|
|
|
- NSData* _remoteAddress;
|
|
|
|
|
|
+NS_ASSUME_NONNULL_BEGIN
|
|
|
|
+
|
|
|
|
+@interface GCDWebServerConnection (Read)
|
|
|
|
+- (void)readData:(NSMutableData*)data withLength:(NSUInteger)length completionBlock:(ReadDataCompletionBlock)block;
|
|
|
|
+- (void)readHeaders:(NSMutableData*)headersData withCompletionBlock:(ReadHeadersCompletionBlock)block;
|
|
|
|
+- (void)readBodyWithRemainingLength:(NSUInteger)length completionBlock:(ReadBodyCompletionBlock)block;
|
|
|
|
+- (void)readNextBodyChunk:(NSMutableData*)chunkData completionBlock:(ReadBodyCompletionBlock)block;
|
|
|
|
+@end
|
|
|
|
+
|
|
|
|
+@interface GCDWebServerConnection (Write)
|
|
|
|
+- (void)writeData:(NSData*)data withCompletionBlock:(WriteDataCompletionBlock)block;
|
|
|
|
+- (void)writeHeadersWithCompletionBlock:(WriteHeadersCompletionBlock)block;
|
|
|
|
+- (void)writeBodyWithCompletionBlock:(WriteBodyCompletionBlock)block;
|
|
|
|
+@end
|
|
|
|
+
|
|
|
|
+NS_ASSUME_NONNULL_END
|
|
|
|
+
|
|
|
|
+@implementation GCDWebServerConnection {
|
|
CFSocketNativeHandle _socket;
|
|
CFSocketNativeHandle _socket;
|
|
- NSUInteger _bytesRead;
|
|
|
|
- NSUInteger _bytesWritten;
|
|
|
|
BOOL _virtualHEAD;
|
|
BOOL _virtualHEAD;
|
|
|
|
|
|
CFHTTPMessageRef _requestMessage;
|
|
CFHTTPMessageRef _requestMessage;
|
|
@@ -83,266 +94,6 @@ static int32_t _connectionCounter = 0;
|
|
int _responseFD;
|
|
int _responseFD;
|
|
#endif
|
|
#endif
|
|
}
|
|
}
|
|
-@end
|
|
|
|
-
|
|
|
|
-@implementation GCDWebServerConnection (Read)
|
|
|
|
-
|
|
|
|
-- (void)_readData:(NSMutableData*)data withLength:(NSUInteger)length completionBlock:(ReadDataCompletionBlock)block {
|
|
|
|
- dispatch_read(_socket, length, dispatch_get_global_queue(_server.dispatchQueuePriority, 0), ^(dispatch_data_t buffer, int error) {
|
|
|
|
-
|
|
|
|
- @autoreleasepool {
|
|
|
|
- if (error == 0) {
|
|
|
|
- size_t size = dispatch_data_get_size(buffer);
|
|
|
|
- if (size > 0) {
|
|
|
|
- NSUInteger originalLength = data.length;
|
|
|
|
- dispatch_data_apply(buffer, ^bool(dispatch_data_t region, size_t chunkOffset, const void* chunkBytes, size_t chunkSize) {
|
|
|
|
- [data appendBytes:chunkBytes length:chunkSize];
|
|
|
|
- return true;
|
|
|
|
- });
|
|
|
|
- [self didReadBytes:((char*)data.bytes + originalLength) length:(data.length - originalLength)];
|
|
|
|
- block(YES);
|
|
|
|
- } else {
|
|
|
|
- if (_bytesRead > 0) {
|
|
|
|
- GWS_LOG_ERROR(@"No more data available on socket %i", _socket);
|
|
|
|
- } else {
|
|
|
|
- GWS_LOG_WARNING(@"No data received from socket %i", _socket);
|
|
|
|
- }
|
|
|
|
- block(NO);
|
|
|
|
- }
|
|
|
|
- } else {
|
|
|
|
- GWS_LOG_ERROR(@"Error while reading from socket %i: %s (%i)", _socket, strerror(error), error);
|
|
|
|
- block(NO);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- });
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-- (void)_readHeaders:(NSMutableData*)headersData withCompletionBlock:(ReadHeadersCompletionBlock)block {
|
|
|
|
- GWS_DCHECK(_requestMessage);
|
|
|
|
- [self _readData:headersData
|
|
|
|
- withLength:NSUIntegerMax
|
|
|
|
- completionBlock:^(BOOL success) {
|
|
|
|
-
|
|
|
|
- if (success) {
|
|
|
|
- NSRange range = [headersData rangeOfData:_CRLFCRLFData options:0 range:NSMakeRange(0, headersData.length)];
|
|
|
|
- if (range.location == NSNotFound) {
|
|
|
|
- [self _readHeaders:headersData withCompletionBlock:block];
|
|
|
|
- } else {
|
|
|
|
- NSUInteger length = range.location + range.length;
|
|
|
|
- if (CFHTTPMessageAppendBytes(_requestMessage, headersData.bytes, length)) {
|
|
|
|
- if (CFHTTPMessageIsHeaderComplete(_requestMessage)) {
|
|
|
|
- block([headersData subdataWithRange:NSMakeRange(length, headersData.length - length)]);
|
|
|
|
- } else {
|
|
|
|
- GWS_LOG_ERROR(@"Failed parsing request headers from socket %i", _socket);
|
|
|
|
- block(nil);
|
|
|
|
- }
|
|
|
|
- } else {
|
|
|
|
- GWS_LOG_ERROR(@"Failed appending request headers data from socket %i", _socket);
|
|
|
|
- block(nil);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- } else {
|
|
|
|
- block(nil);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- }];
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-- (void)_readBodyWithRemainingLength:(NSUInteger)length completionBlock:(ReadBodyCompletionBlock)block {
|
|
|
|
- GWS_DCHECK([_request hasBody] && ![_request usesChunkedTransferEncoding]);
|
|
|
|
- NSMutableData* bodyData = [[NSMutableData alloc] initWithCapacity:kBodyReadCapacity];
|
|
|
|
- [self _readData:bodyData
|
|
|
|
- withLength:length
|
|
|
|
- completionBlock:^(BOOL success) {
|
|
|
|
-
|
|
|
|
- if (success) {
|
|
|
|
- if (bodyData.length <= length) {
|
|
|
|
- NSError* error = nil;
|
|
|
|
- if ([_request performWriteData:bodyData error:&error]) {
|
|
|
|
- NSUInteger remainingLength = length - bodyData.length;
|
|
|
|
- if (remainingLength) {
|
|
|
|
- [self _readBodyWithRemainingLength:remainingLength completionBlock:block];
|
|
|
|
- } else {
|
|
|
|
- block(YES);
|
|
|
|
- }
|
|
|
|
- } else {
|
|
|
|
- GWS_LOG_ERROR(@"Failed writing request body on socket %i: %@", _socket, error);
|
|
|
|
- block(NO);
|
|
|
|
- }
|
|
|
|
- } else {
|
|
|
|
- GWS_LOG_ERROR(@"Unexpected extra content reading request body on socket %i", _socket);
|
|
|
|
- block(NO);
|
|
|
|
- GWS_DNOT_REACHED();
|
|
|
|
- }
|
|
|
|
- } else {
|
|
|
|
- block(NO);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- }];
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
|
|
|
|
- char buffer[size + 1];
|
|
|
|
- bcopy(bytes, buffer, size);
|
|
|
|
- buffer[size] = 0;
|
|
|
|
- char* end = NULL;
|
|
|
|
- long result = strtol(buffer, &end, 16);
|
|
|
|
- return ((end != NULL) && (*end == 0) && (result >= 0) ? result : NSNotFound);
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-- (void)_readNextBodyChunk:(NSMutableData*)chunkData completionBlock:(ReadBodyCompletionBlock)block {
|
|
|
|
- GWS_DCHECK([_request hasBody] && [_request usesChunkedTransferEncoding]);
|
|
|
|
-
|
|
|
|
- while (1) {
|
|
|
|
- NSRange range = [chunkData rangeOfData:_CRLFData options:0 range:NSMakeRange(0, chunkData.length)];
|
|
|
|
- if (range.location == NSNotFound) {
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
- NSRange extensionRange = [chunkData rangeOfData:[NSData dataWithBytes:";" length:1] options:0 range:NSMakeRange(0, range.location)]; // Ignore chunk extensions
|
|
|
|
- NSUInteger length = _ScanHexNumber((char*)chunkData.bytes, extensionRange.location != NSNotFound ? extensionRange.location : range.location);
|
|
|
|
- if (length != NSNotFound) {
|
|
|
|
- if (length) {
|
|
|
|
- if (chunkData.length < range.location + range.length + length + 2) {
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
- const char* ptr = (char*)chunkData.bytes + range.location + range.length + length;
|
|
|
|
- if ((*ptr == '\r') && (*(ptr + 1) == '\n')) {
|
|
|
|
- NSError* error = nil;
|
|
|
|
- if ([_request performWriteData:[chunkData subdataWithRange:NSMakeRange(range.location + range.length, length)] error:&error]) {
|
|
|
|
- [chunkData replaceBytesInRange:NSMakeRange(0, range.location + range.length + length + 2) withBytes:NULL length:0];
|
|
|
|
- } else {
|
|
|
|
- GWS_LOG_ERROR(@"Failed writing request body on socket %i: %@", _socket, error);
|
|
|
|
- block(NO);
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
- } else {
|
|
|
|
- GWS_LOG_ERROR(@"Missing terminating CRLF sequence for chunk reading request body on socket %i", _socket);
|
|
|
|
- block(NO);
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
- } else {
|
|
|
|
- NSRange trailerRange = [chunkData rangeOfData:_CRLFCRLFData options:0 range:NSMakeRange(range.location, chunkData.length - range.location)]; // Ignore trailers
|
|
|
|
- if (trailerRange.location != NSNotFound) {
|
|
|
|
- block(YES);
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- } else {
|
|
|
|
- GWS_LOG_ERROR(@"Invalid chunk length reading request body on socket %i", _socket);
|
|
|
|
- block(NO);
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- [self _readData:chunkData
|
|
|
|
- withLength:NSUIntegerMax
|
|
|
|
- completionBlock:^(BOOL success) {
|
|
|
|
-
|
|
|
|
- if (success) {
|
|
|
|
- [self _readNextBodyChunk:chunkData completionBlock:block];
|
|
|
|
- } else {
|
|
|
|
- block(NO);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- }];
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-@end
|
|
|
|
-
|
|
|
|
-@implementation GCDWebServerConnection (Write)
|
|
|
|
-
|
|
|
|
-- (void)_writeData:(NSData*)data withCompletionBlock:(WriteDataCompletionBlock)block {
|
|
|
|
- dispatch_data_t buffer = dispatch_data_create(data.bytes, data.length, dispatch_get_global_queue(_server.dispatchQueuePriority, 0), ^{
|
|
|
|
- [data self]; // Keeps ARC from releasing data too early
|
|
|
|
- });
|
|
|
|
- dispatch_write(_socket, buffer, dispatch_get_global_queue(_server.dispatchQueuePriority, 0), ^(dispatch_data_t remainingData, int error) {
|
|
|
|
-
|
|
|
|
- @autoreleasepool {
|
|
|
|
- if (error == 0) {
|
|
|
|
- GWS_DCHECK(remainingData == NULL);
|
|
|
|
- [self didWriteBytes:data.bytes length:data.length];
|
|
|
|
- block(YES);
|
|
|
|
- } else {
|
|
|
|
- GWS_LOG_ERROR(@"Error while writing to socket %i: %s (%i)", _socket, strerror(error), error);
|
|
|
|
- block(NO);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- });
|
|
|
|
-#if !OS_OBJECT_USE_OBJC_RETAIN_RELEASE
|
|
|
|
- dispatch_release(buffer);
|
|
|
|
-#endif
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-- (void)_writeHeadersWithCompletionBlock:(WriteHeadersCompletionBlock)block {
|
|
|
|
- GWS_DCHECK(_responseMessage);
|
|
|
|
- CFDataRef data = CFHTTPMessageCopySerializedMessage(_responseMessage);
|
|
|
|
- [self _writeData:(__bridge NSData*)data withCompletionBlock:block];
|
|
|
|
- CFRelease(data);
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-- (void)_writeBodyWithCompletionBlock:(WriteBodyCompletionBlock)block {
|
|
|
|
- GWS_DCHECK([_response hasBody]);
|
|
|
|
- [_response performReadDataWithCompletion:^(NSData* data, NSError* error) {
|
|
|
|
-
|
|
|
|
- if (data) {
|
|
|
|
- if (data.length) {
|
|
|
|
- if (_response.usesChunkedTransferEncoding) {
|
|
|
|
- const char* hexString = [[NSString stringWithFormat:@"%lx", (unsigned long)data.length] UTF8String];
|
|
|
|
- size_t hexLength = strlen(hexString);
|
|
|
|
- NSData* chunk = [NSMutableData dataWithLength:(hexLength + 2 + data.length + 2)];
|
|
|
|
- if (chunk == nil) {
|
|
|
|
- GWS_LOG_ERROR(@"Failed allocating memory for response body chunk for socket %i: %@", _socket, error);
|
|
|
|
- block(NO);
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
- char* ptr = (char*)[(NSMutableData*)chunk mutableBytes];
|
|
|
|
- bcopy(hexString, ptr, hexLength);
|
|
|
|
- ptr += hexLength;
|
|
|
|
- *ptr++ = '\r';
|
|
|
|
- *ptr++ = '\n';
|
|
|
|
- bcopy(data.bytes, ptr, data.length);
|
|
|
|
- ptr += data.length;
|
|
|
|
- *ptr++ = '\r';
|
|
|
|
- *ptr = '\n';
|
|
|
|
- data = chunk;
|
|
|
|
- }
|
|
|
|
- [self _writeData:data
|
|
|
|
- withCompletionBlock:^(BOOL success) {
|
|
|
|
-
|
|
|
|
- if (success) {
|
|
|
|
- [self _writeBodyWithCompletionBlock:block];
|
|
|
|
- } else {
|
|
|
|
- block(NO);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- }];
|
|
|
|
- } else {
|
|
|
|
- if (_response.usesChunkedTransferEncoding) {
|
|
|
|
- [self _writeData:_lastChunkData
|
|
|
|
- withCompletionBlock:^(BOOL success) {
|
|
|
|
-
|
|
|
|
- block(success);
|
|
|
|
-
|
|
|
|
- }];
|
|
|
|
- } else {
|
|
|
|
- block(YES);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- } else {
|
|
|
|
- GWS_LOG_ERROR(@"Failed reading response body for socket %i: %@", _socket, error);
|
|
|
|
- block(NO);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- }];
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-@end
|
|
|
|
-
|
|
|
|
-@implementation GCDWebServerConnection
|
|
|
|
-
|
|
|
|
-@synthesize server = _server, localAddressData = _localAddress, remoteAddressData = _remoteAddress, totalBytesRead = _bytesRead, totalBytesWritten = _bytesWritten;
|
|
|
|
|
|
|
|
+ (void)initialize {
|
|
+ (void)initialize {
|
|
if (_CRLFData == nil) {
|
|
if (_CRLFData == nil) {
|
|
@@ -370,7 +121,7 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
|
|
}
|
|
}
|
|
|
|
|
|
- (BOOL)isUsingIPv6 {
|
|
- (BOOL)isUsingIPv6 {
|
|
- const struct sockaddr* localSockAddr = _localAddress.bytes;
|
|
|
|
|
|
+ const struct sockaddr* localSockAddr = _localAddressData.bytes;
|
|
return (localSockAddr->sa_family == AF_INET6);
|
|
return (localSockAddr->sa_family == AF_INET6);
|
|
}
|
|
}
|
|
|
|
|
|
@@ -420,7 +171,7 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
|
|
if (_response) {
|
|
if (_response) {
|
|
[self _initializeResponseHeadersWithStatusCode:_response.statusCode];
|
|
[self _initializeResponseHeadersWithStatusCode:_response.statusCode];
|
|
if (_response.lastModifiedDate) {
|
|
if (_response.lastModifiedDate) {
|
|
- CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Last-Modified"), (__bridge CFStringRef)GCDWebServerFormatRFC822(_response.lastModifiedDate));
|
|
|
|
|
|
+ CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Last-Modified"), (__bridge CFStringRef)GCDWebServerFormatRFC822((NSDate*)_response.lastModifiedDate));
|
|
}
|
|
}
|
|
if (_response.eTag) {
|
|
if (_response.eTag) {
|
|
CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("ETag"), (__bridge CFStringRef)_response.eTag);
|
|
CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("ETag"), (__bridge CFStringRef)_response.eTag);
|
|
@@ -444,11 +195,11 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
|
|
[_response.additionalHeaders enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL* stop) {
|
|
[_response.additionalHeaders enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL* stop) {
|
|
CFHTTPMessageSetHeaderFieldValue(_responseMessage, (__bridge CFStringRef)key, (__bridge CFStringRef)obj);
|
|
CFHTTPMessageSetHeaderFieldValue(_responseMessage, (__bridge CFStringRef)key, (__bridge CFStringRef)obj);
|
|
}];
|
|
}];
|
|
- [self _writeHeadersWithCompletionBlock:^(BOOL success) {
|
|
|
|
|
|
+ [self writeHeadersWithCompletionBlock:^(BOOL success) {
|
|
|
|
|
|
if (success) {
|
|
if (success) {
|
|
if (hasBody) {
|
|
if (hasBody) {
|
|
- [self _writeBodyWithCompletionBlock:^(BOOL successInner) {
|
|
|
|
|
|
+ [self writeBodyWithCompletionBlock:^(BOOL successInner) {
|
|
|
|
|
|
[_response performClose]; // TODO: There's nothing we can do on failure as headers have already been sent
|
|
[_response performClose]; // TODO: There's nothing we can do on failure as headers have already been sent
|
|
|
|
|
|
@@ -485,18 +236,18 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
|
|
}
|
|
}
|
|
|
|
|
|
if (length) {
|
|
if (length) {
|
|
- [self _readBodyWithRemainingLength:length
|
|
|
|
- completionBlock:^(BOOL success) {
|
|
|
|
-
|
|
|
|
- NSError* localError = nil;
|
|
|
|
- if ([_request performClose:&localError]) {
|
|
|
|
- [self _startProcessingRequest];
|
|
|
|
- } else {
|
|
|
|
- GWS_LOG_ERROR(@"Failed closing request body for socket %i: %@", _socket, error);
|
|
|
|
- [self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError];
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- }];
|
|
|
|
|
|
+ [self readBodyWithRemainingLength:length
|
|
|
|
+ completionBlock:^(BOOL success) {
|
|
|
|
+
|
|
|
|
+ NSError* localError = nil;
|
|
|
|
+ if ([_request performClose:&localError]) {
|
|
|
|
+ [self _startProcessingRequest];
|
|
|
|
+ } else {
|
|
|
|
+ GWS_LOG_ERROR(@"Failed closing request body for socket %i: %@", _socket, error);
|
|
|
|
+ [self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError];
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }];
|
|
} else {
|
|
} else {
|
|
if ([_request performClose:&error]) {
|
|
if ([_request performClose:&error]) {
|
|
[self _startProcessingRequest];
|
|
[self _startProcessingRequest];
|
|
@@ -516,24 +267,24 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
|
|
}
|
|
}
|
|
|
|
|
|
NSMutableData* chunkData = [[NSMutableData alloc] initWithData:initialData];
|
|
NSMutableData* chunkData = [[NSMutableData alloc] initWithData:initialData];
|
|
- [self _readNextBodyChunk:chunkData
|
|
|
|
- completionBlock:^(BOOL success) {
|
|
|
|
-
|
|
|
|
- NSError* localError = nil;
|
|
|
|
- if ([_request performClose:&localError]) {
|
|
|
|
- [self _startProcessingRequest];
|
|
|
|
- } else {
|
|
|
|
- GWS_LOG_ERROR(@"Failed closing request body for socket %i: %@", _socket, error);
|
|
|
|
- [self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError];
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- }];
|
|
|
|
|
|
+ [self readNextBodyChunk:chunkData
|
|
|
|
+ completionBlock:^(BOOL success) {
|
|
|
|
+
|
|
|
|
+ NSError* localError = nil;
|
|
|
|
+ if ([_request performClose:&localError]) {
|
|
|
|
+ [self _startProcessingRequest];
|
|
|
|
+ } else {
|
|
|
|
+ GWS_LOG_ERROR(@"Failed closing request body for socket %i: %@", _socket, error);
|
|
|
|
+ [self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError];
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }];
|
|
}
|
|
}
|
|
|
|
|
|
- (void)_readRequestHeaders {
|
|
- (void)_readRequestHeaders {
|
|
_requestMessage = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, true);
|
|
_requestMessage = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, true);
|
|
NSMutableData* headersData = [[NSMutableData alloc] initWithCapacity:kHeadersReadCapacity];
|
|
NSMutableData* headersData = [[NSMutableData alloc] initWithCapacity:kHeadersReadCapacity];
|
|
- [self _readHeaders:headersData
|
|
|
|
|
|
+ [self readHeaders:headersData
|
|
withCompletionBlock:^(NSData* extraData) {
|
|
withCompletionBlock:^(NSData* extraData) {
|
|
|
|
|
|
if (extraData) {
|
|
if (extraData) {
|
|
@@ -548,7 +299,8 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
|
|
requestURL = [self rewriteRequestURL:requestURL withMethod:requestMethod headers:requestHeaders];
|
|
requestURL = [self rewriteRequestURL:requestURL withMethod:requestMethod headers:requestHeaders];
|
|
GWS_DCHECK(requestURL);
|
|
GWS_DCHECK(requestURL);
|
|
}
|
|
}
|
|
- NSString* requestPath = requestURL ? GCDWebServerUnescapeURLString(CFBridgingRelease(CFURLCopyPath((CFURLRef)requestURL))) : nil; // Don't use -[NSURL path] which strips the ending slash
|
|
|
|
|
|
+ NSString* urlPath = requestURL ? CFBridgingRelease(CFURLCopyPath((CFURLRef)requestURL)) : nil; // Don't use -[NSURL path] which strips the ending slash
|
|
|
|
+ NSString* requestPath = urlPath ? GCDWebServerUnescapeURLString(urlPath) : nil;
|
|
NSString* queryString = requestURL ? CFBridgingRelease(CFURLCopyQueryString((CFURLRef)requestURL, NULL)) : nil; // Don't use -[NSURL query] to make sure query is not unescaped;
|
|
NSString* queryString = requestURL ? CFBridgingRelease(CFURLCopyQueryString((CFURLRef)requestURL, NULL)) : nil; // Don't use -[NSURL query] to make sure query is not unescaped;
|
|
NSDictionary* requestQuery = queryString ? GCDWebServerParseURLEncodedForm(queryString) : @{};
|
|
NSDictionary* requestQuery = queryString ? GCDWebServerParseURLEncodedForm(queryString) : @{};
|
|
if (requestMethod && requestURL && requestHeaders && requestPath && requestQuery) {
|
|
if (requestMethod && requestURL && requestHeaders && requestPath && requestQuery) {
|
|
@@ -567,7 +319,7 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
|
|
NSString* expectHeader = [requestHeaders objectForKey:@"Expect"];
|
|
NSString* expectHeader = [requestHeaders objectForKey:@"Expect"];
|
|
if (expectHeader) {
|
|
if (expectHeader) {
|
|
if ([expectHeader caseInsensitiveCompare:@"100-continue"] == NSOrderedSame) { // TODO: Actually validate request before continuing
|
|
if ([expectHeader caseInsensitiveCompare:@"100-continue"] == NSOrderedSame) { // TODO: Actually validate request before continuing
|
|
- [self _writeData:_continueData
|
|
|
|
|
|
+ [self writeData:_continueData
|
|
withCompletionBlock:^(BOOL success) {
|
|
withCompletionBlock:^(BOOL success) {
|
|
|
|
|
|
if (success) {
|
|
if (success) {
|
|
@@ -613,11 +365,11 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
|
|
}];
|
|
}];
|
|
}
|
|
}
|
|
|
|
|
|
-- (id)initWithServer:(GCDWebServer*)server localAddress:(NSData*)localAddress remoteAddress:(NSData*)remoteAddress socket:(CFSocketNativeHandle)socket {
|
|
|
|
|
|
+- (instancetype)initWithServer:(GCDWebServer*)server localAddress:(NSData*)localAddress remoteAddress:(NSData*)remoteAddress socket:(CFSocketNativeHandle)socket {
|
|
if ((self = [super init])) {
|
|
if ((self = [super init])) {
|
|
_server = server;
|
|
_server = server;
|
|
- _localAddress = localAddress;
|
|
|
|
- _remoteAddress = remoteAddress;
|
|
|
|
|
|
+ _localAddressData = localAddress;
|
|
|
|
+ _remoteAddressData = remoteAddress;
|
|
_socket = socket;
|
|
_socket = socket;
|
|
GWS_LOG_DEBUG(@"Did open connection on socket %i", _socket);
|
|
GWS_LOG_DEBUG(@"Did open connection on socket %i", _socket);
|
|
|
|
|
|
@@ -635,11 +387,11 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
|
|
}
|
|
}
|
|
|
|
|
|
- (NSString*)localAddressString {
|
|
- (NSString*)localAddressString {
|
|
- return GCDWebServerStringFromSockAddr(_localAddress.bytes, YES);
|
|
|
|
|
|
+ return GCDWebServerStringFromSockAddr(_localAddressData.bytes, YES);
|
|
}
|
|
}
|
|
|
|
|
|
- (NSString*)remoteAddressString {
|
|
- (NSString*)remoteAddressString {
|
|
- return GCDWebServerStringFromSockAddr(_remoteAddress.bytes, YES);
|
|
|
|
|
|
+ return GCDWebServerStringFromSockAddr(_remoteAddressData.bytes, YES);
|
|
}
|
|
}
|
|
|
|
|
|
- (void)dealloc {
|
|
- (void)dealloc {
|
|
@@ -667,6 +419,261 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
|
|
|
|
|
|
@end
|
|
@end
|
|
|
|
|
|
|
|
+@implementation GCDWebServerConnection (Read)
|
|
|
|
+
|
|
|
|
+- (void)readData:(NSMutableData*)data withLength:(NSUInteger)length completionBlock:(ReadDataCompletionBlock)block {
|
|
|
|
+ dispatch_read(_socket, length, dispatch_get_global_queue(_server.dispatchQueuePriority, 0), ^(dispatch_data_t buffer, int error) {
|
|
|
|
+
|
|
|
|
+ @autoreleasepool {
|
|
|
|
+ if (error == 0) {
|
|
|
|
+ size_t size = dispatch_data_get_size(buffer);
|
|
|
|
+ if (size > 0) {
|
|
|
|
+ NSUInteger originalLength = data.length;
|
|
|
|
+ dispatch_data_apply(buffer, ^bool(dispatch_data_t region, size_t chunkOffset, const void* chunkBytes, size_t chunkSize) {
|
|
|
|
+ [data appendBytes:chunkBytes length:chunkSize];
|
|
|
|
+ return true;
|
|
|
|
+ });
|
|
|
|
+ [self didReadBytes:((char*)data.bytes + originalLength) length:(data.length - originalLength)];
|
|
|
|
+ block(YES);
|
|
|
|
+ } else {
|
|
|
|
+ if (_totalBytesRead > 0) {
|
|
|
|
+ GWS_LOG_ERROR(@"No more data available on socket %i", _socket);
|
|
|
|
+ } else {
|
|
|
|
+ GWS_LOG_WARNING(@"No data received from socket %i", _socket);
|
|
|
|
+ }
|
|
|
|
+ block(NO);
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ GWS_LOG_ERROR(@"Error while reading from socket %i: %s (%i)", _socket, strerror(error), error);
|
|
|
|
+ block(NO);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ });
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+- (void)readHeaders:(NSMutableData*)headersData withCompletionBlock:(ReadHeadersCompletionBlock)block {
|
|
|
|
+ GWS_DCHECK(_requestMessage);
|
|
|
|
+ [self readData:headersData
|
|
|
|
+ withLength:NSUIntegerMax
|
|
|
|
+ completionBlock:^(BOOL success) {
|
|
|
|
+
|
|
|
|
+ if (success) {
|
|
|
|
+ NSRange range = [headersData rangeOfData:_CRLFCRLFData options:0 range:NSMakeRange(0, headersData.length)];
|
|
|
|
+ if (range.location == NSNotFound) {
|
|
|
|
+ [self readHeaders:headersData withCompletionBlock:block];
|
|
|
|
+ } else {
|
|
|
|
+ NSUInteger length = range.location + range.length;
|
|
|
|
+ if (CFHTTPMessageAppendBytes(_requestMessage, headersData.bytes, length)) {
|
|
|
|
+ if (CFHTTPMessageIsHeaderComplete(_requestMessage)) {
|
|
|
|
+ block([headersData subdataWithRange:NSMakeRange(length, headersData.length - length)]);
|
|
|
|
+ } else {
|
|
|
|
+ GWS_LOG_ERROR(@"Failed parsing request headers from socket %i", _socket);
|
|
|
|
+ block(nil);
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ GWS_LOG_ERROR(@"Failed appending request headers data from socket %i", _socket);
|
|
|
|
+ block(nil);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ block(nil);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }];
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+- (void)readBodyWithRemainingLength:(NSUInteger)length completionBlock:(ReadBodyCompletionBlock)block {
|
|
|
|
+ GWS_DCHECK([_request hasBody] && ![_request usesChunkedTransferEncoding]);
|
|
|
|
+ NSMutableData* bodyData = [[NSMutableData alloc] initWithCapacity:kBodyReadCapacity];
|
|
|
|
+ [self readData:bodyData
|
|
|
|
+ withLength:length
|
|
|
|
+ completionBlock:^(BOOL success) {
|
|
|
|
+
|
|
|
|
+ if (success) {
|
|
|
|
+ if (bodyData.length <= length) {
|
|
|
|
+ NSError* error = nil;
|
|
|
|
+ if ([_request performWriteData:bodyData error:&error]) {
|
|
|
|
+ NSUInteger remainingLength = length - bodyData.length;
|
|
|
|
+ if (remainingLength) {
|
|
|
|
+ [self readBodyWithRemainingLength:remainingLength completionBlock:block];
|
|
|
|
+ } else {
|
|
|
|
+ block(YES);
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ GWS_LOG_ERROR(@"Failed writing request body on socket %i: %@", _socket, error);
|
|
|
|
+ block(NO);
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ GWS_LOG_ERROR(@"Unexpected extra content reading request body on socket %i", _socket);
|
|
|
|
+ block(NO);
|
|
|
|
+ GWS_DNOT_REACHED();
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ block(NO);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }];
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
|
|
|
|
+ char buffer[size + 1];
|
|
|
|
+ bcopy(bytes, buffer, size);
|
|
|
|
+ buffer[size] = 0;
|
|
|
|
+ char* end = NULL;
|
|
|
|
+ long result = strtol(buffer, &end, 16);
|
|
|
|
+ return ((end != NULL) && (*end == 0) && (result >= 0) ? result : NSNotFound);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+- (void)readNextBodyChunk:(NSMutableData*)chunkData completionBlock:(ReadBodyCompletionBlock)block {
|
|
|
|
+ GWS_DCHECK([_request hasBody] && [_request usesChunkedTransferEncoding]);
|
|
|
|
+
|
|
|
|
+ while (1) {
|
|
|
|
+ NSRange range = [chunkData rangeOfData:_CRLFData options:0 range:NSMakeRange(0, chunkData.length)];
|
|
|
|
+ if (range.location == NSNotFound) {
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ NSRange extensionRange = [chunkData rangeOfData:[NSData dataWithBytes:";" length:1] options:0 range:NSMakeRange(0, range.location)]; // Ignore chunk extensions
|
|
|
|
+ NSUInteger length = _ScanHexNumber((char*)chunkData.bytes, extensionRange.location != NSNotFound ? extensionRange.location : range.location);
|
|
|
|
+ if (length != NSNotFound) {
|
|
|
|
+ if (length) {
|
|
|
|
+ if (chunkData.length < range.location + range.length + length + 2) {
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ const char* ptr = (char*)chunkData.bytes + range.location + range.length + length;
|
|
|
|
+ if ((*ptr == '\r') && (*(ptr + 1) == '\n')) {
|
|
|
|
+ NSError* error = nil;
|
|
|
|
+ if ([_request performWriteData:[chunkData subdataWithRange:NSMakeRange(range.location + range.length, length)] error:&error]) {
|
|
|
|
+ [chunkData replaceBytesInRange:NSMakeRange(0, range.location + range.length + length + 2) withBytes:NULL length:0];
|
|
|
|
+ } else {
|
|
|
|
+ GWS_LOG_ERROR(@"Failed writing request body on socket %i: %@", _socket, error);
|
|
|
|
+ block(NO);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ GWS_LOG_ERROR(@"Missing terminating CRLF sequence for chunk reading request body on socket %i", _socket);
|
|
|
|
+ block(NO);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ NSRange trailerRange = [chunkData rangeOfData:_CRLFCRLFData options:0 range:NSMakeRange(range.location, chunkData.length - range.location)]; // Ignore trailers
|
|
|
|
+ if (trailerRange.location != NSNotFound) {
|
|
|
|
+ block(YES);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ GWS_LOG_ERROR(@"Invalid chunk length reading request body on socket %i", _socket);
|
|
|
|
+ block(NO);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ [self readData:chunkData
|
|
|
|
+ withLength:NSUIntegerMax
|
|
|
|
+ completionBlock:^(BOOL success) {
|
|
|
|
+
|
|
|
|
+ if (success) {
|
|
|
|
+ [self readNextBodyChunk:chunkData completionBlock:block];
|
|
|
|
+ } else {
|
|
|
|
+ block(NO);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }];
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+@end
|
|
|
|
+
|
|
|
|
+@implementation GCDWebServerConnection (Write)
|
|
|
|
+
|
|
|
|
+- (void)writeData:(NSData*)data withCompletionBlock:(WriteDataCompletionBlock)block {
|
|
|
|
+ dispatch_data_t buffer = dispatch_data_create(data.bytes, data.length, dispatch_get_global_queue(_server.dispatchQueuePriority, 0), ^{
|
|
|
|
+ [data self]; // Keeps ARC from releasing data too early
|
|
|
|
+ });
|
|
|
|
+ dispatch_write(_socket, buffer, dispatch_get_global_queue(_server.dispatchQueuePriority, 0), ^(dispatch_data_t remainingData, int error) {
|
|
|
|
+
|
|
|
|
+ @autoreleasepool {
|
|
|
|
+ if (error == 0) {
|
|
|
|
+ GWS_DCHECK(remainingData == NULL);
|
|
|
|
+ [self didWriteBytes:data.bytes length:data.length];
|
|
|
|
+ block(YES);
|
|
|
|
+ } else {
|
|
|
|
+ GWS_LOG_ERROR(@"Error while writing to socket %i: %s (%i)", _socket, strerror(error), error);
|
|
|
|
+ block(NO);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ });
|
|
|
|
+#if !OS_OBJECT_USE_OBJC_RETAIN_RELEASE
|
|
|
|
+ dispatch_release(buffer);
|
|
|
|
+#endif
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+- (void)writeHeadersWithCompletionBlock:(WriteHeadersCompletionBlock)block {
|
|
|
|
+ GWS_DCHECK(_responseMessage);
|
|
|
|
+ CFDataRef data = CFHTTPMessageCopySerializedMessage(_responseMessage);
|
|
|
|
+ [self writeData:(__bridge NSData*)data withCompletionBlock:block];
|
|
|
|
+ CFRelease(data);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+- (void)writeBodyWithCompletionBlock:(WriteBodyCompletionBlock)block {
|
|
|
|
+ GWS_DCHECK([_response hasBody]);
|
|
|
|
+ [_response performReadDataWithCompletion:^(NSData* data, NSError* error) {
|
|
|
|
+
|
|
|
|
+ if (data) {
|
|
|
|
+ if (data.length) {
|
|
|
|
+ if (_response.usesChunkedTransferEncoding) {
|
|
|
|
+ const char* hexString = [[NSString stringWithFormat:@"%lx", (unsigned long)data.length] UTF8String];
|
|
|
|
+ size_t hexLength = strlen(hexString);
|
|
|
|
+ NSData* chunk = [NSMutableData dataWithLength:(hexLength + 2 + data.length + 2)];
|
|
|
|
+ if (chunk == nil) {
|
|
|
|
+ GWS_LOG_ERROR(@"Failed allocating memory for response body chunk for socket %i: %@", _socket, error);
|
|
|
|
+ block(NO);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ char* ptr = (char*)[(NSMutableData*)chunk mutableBytes];
|
|
|
|
+ bcopy(hexString, ptr, hexLength);
|
|
|
|
+ ptr += hexLength;
|
|
|
|
+ *ptr++ = '\r';
|
|
|
|
+ *ptr++ = '\n';
|
|
|
|
+ bcopy(data.bytes, ptr, data.length);
|
|
|
|
+ ptr += data.length;
|
|
|
|
+ *ptr++ = '\r';
|
|
|
|
+ *ptr = '\n';
|
|
|
|
+ data = chunk;
|
|
|
|
+ }
|
|
|
|
+ [self writeData:data
|
|
|
|
+ withCompletionBlock:^(BOOL success) {
|
|
|
|
+
|
|
|
|
+ if (success) {
|
|
|
|
+ [self writeBodyWithCompletionBlock:block];
|
|
|
|
+ } else {
|
|
|
|
+ block(NO);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }];
|
|
|
|
+ } else {
|
|
|
|
+ if (_response.usesChunkedTransferEncoding) {
|
|
|
|
+ [self writeData:_lastChunkData
|
|
|
|
+ withCompletionBlock:^(BOOL success) {
|
|
|
|
+
|
|
|
|
+ block(success);
|
|
|
|
+
|
|
|
|
+ }];
|
|
|
|
+ } else {
|
|
|
|
+ block(YES);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ GWS_LOG_ERROR(@"Failed reading response body for socket %i: %@", _socket, error);
|
|
|
|
+ block(NO);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }];
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+@end
|
|
|
|
+
|
|
@implementation GCDWebServerConnection (Subclassing)
|
|
@implementation GCDWebServerConnection (Subclassing)
|
|
|
|
|
|
- (BOOL)open {
|
|
- (BOOL)open {
|
|
@@ -692,7 +699,7 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
|
|
|
|
|
|
- (void)didReadBytes:(const void*)bytes length:(NSUInteger)length {
|
|
- (void)didReadBytes:(const void*)bytes length:(NSUInteger)length {
|
|
GWS_LOG_DEBUG(@"Connection received %lu bytes on socket %i", (unsigned long)length, _socket);
|
|
GWS_LOG_DEBUG(@"Connection received %lu bytes on socket %i", (unsigned long)length, _socket);
|
|
- _bytesRead += length;
|
|
|
|
|
|
+ _totalBytesRead += length;
|
|
|
|
|
|
#ifdef __GCDWEBSERVER_ENABLE_TESTING__
|
|
#ifdef __GCDWEBSERVER_ENABLE_TESTING__
|
|
if ((_requestFD > 0) && (write(_requestFD, bytes, length) != (ssize_t)length)) {
|
|
if ((_requestFD > 0) && (write(_requestFD, bytes, length) != (ssize_t)length)) {
|
|
@@ -705,7 +712,7 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
|
|
|
|
|
|
- (void)didWriteBytes:(const void*)bytes length:(NSUInteger)length {
|
|
- (void)didWriteBytes:(const void*)bytes length:(NSUInteger)length {
|
|
GWS_LOG_DEBUG(@"Connection sent %lu bytes on socket %i", (unsigned long)length, _socket);
|
|
GWS_LOG_DEBUG(@"Connection sent %lu bytes on socket %i", (unsigned long)length, _socket);
|
|
- _bytesWritten += length;
|
|
|
|
|
|
+ _totalBytesWritten += length;
|
|
|
|
|
|
#ifdef __GCDWEBSERVER_ENABLE_TESTING__
|
|
#ifdef __GCDWEBSERVER_ENABLE_TESTING__
|
|
if ((_responseFD > 0) && (write(_responseFD, bytes, length) != (ssize_t)length)) {
|
|
if ((_responseFD > 0) && (write(_responseFD, bytes, length) != (ssize_t)length)) {
|
|
@@ -722,7 +729,7 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
|
|
|
|
|
|
// https://tools.ietf.org/html/rfc2617
|
|
// https://tools.ietf.org/html/rfc2617
|
|
- (GCDWebServerResponse*)preflightRequest:(GCDWebServerRequest*)request {
|
|
- (GCDWebServerResponse*)preflightRequest:(GCDWebServerRequest*)request {
|
|
- GWS_LOG_DEBUG(@"Connection on socket %i preflighting request \"%@ %@\" with %lu bytes body", _socket, _virtualHEAD ? @"HEAD" : _request.method, _request.path, (unsigned long)_bytesRead);
|
|
|
|
|
|
+ GWS_LOG_DEBUG(@"Connection on socket %i preflighting request \"%@ %@\" with %lu bytes body", _socket, _virtualHEAD ? @"HEAD" : _request.method, _request.path, (unsigned long)_totalBytesRead);
|
|
GCDWebServerResponse* response = nil;
|
|
GCDWebServerResponse* response = nil;
|
|
if (_server.authenticationBasicAccounts) {
|
|
if (_server.authenticationBasicAccounts) {
|
|
__block BOOL authenticated = NO;
|
|
__block BOOL authenticated = NO;
|
|
@@ -772,7 +779,7 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
|
|
}
|
|
}
|
|
|
|
|
|
- (void)processRequest:(GCDWebServerRequest*)request completion:(GCDWebServerCompletionBlock)completion {
|
|
- (void)processRequest:(GCDWebServerRequest*)request completion:(GCDWebServerCompletionBlock)completion {
|
|
- GWS_LOG_DEBUG(@"Connection on socket %i processing request \"%@ %@\" with %lu bytes body", _socket, _virtualHEAD ? @"HEAD" : _request.method, _request.path, (unsigned long)_bytesRead);
|
|
|
|
|
|
+ GWS_LOG_DEBUG(@"Connection on socket %i processing request \"%@ %@\" with %lu bytes body", _socket, _virtualHEAD ? @"HEAD" : _request.method, _request.path, (unsigned long)_totalBytesRead);
|
|
_handler.asyncProcessBlock(request, [completion copy]);
|
|
_handler.asyncProcessBlock(request, [completion copy]);
|
|
}
|
|
}
|
|
|
|
|
|
@@ -812,7 +819,7 @@ static inline BOOL _CompareResources(NSString* responseETag, NSString* requestET
|
|
GWS_DCHECK(_responseMessage == NULL);
|
|
GWS_DCHECK(_responseMessage == NULL);
|
|
GWS_DCHECK((statusCode >= 400) && (statusCode < 600));
|
|
GWS_DCHECK((statusCode >= 400) && (statusCode < 600));
|
|
[self _initializeResponseHeadersWithStatusCode:statusCode];
|
|
[self _initializeResponseHeadersWithStatusCode:statusCode];
|
|
- [self _writeHeadersWithCompletionBlock:^(BOOL success) {
|
|
|
|
|
|
+ [self writeHeadersWithCompletionBlock:^(BOOL success) {
|
|
; // Nothing more to do
|
|
; // Nothing more to do
|
|
}];
|
|
}];
|
|
GWS_LOG_DEBUG(@"Connection aborted with status code %i on socket %i", (int)statusCode, _socket);
|
|
GWS_LOG_DEBUG(@"Connection aborted with status code %i on socket %i", (int)statusCode, _socket);
|
|
@@ -852,9 +859,9 @@ static inline BOOL _CompareResources(NSString* responseETag, NSString* requestET
|
|
#endif
|
|
#endif
|
|
|
|
|
|
if (_request) {
|
|
if (_request) {
|
|
- GWS_LOG_VERBOSE(@"[%@] %@ %i \"%@ %@\" (%lu | %lu)", self.localAddressString, self.remoteAddressString, (int)_statusCode, _virtualHEAD ? @"HEAD" : _request.method, _request.path, (unsigned long)_bytesRead, (unsigned long)_bytesWritten);
|
|
|
|
|
|
+ GWS_LOG_VERBOSE(@"[%@] %@ %i \"%@ %@\" (%lu | %lu)", self.localAddressString, self.remoteAddressString, (int)_statusCode, _virtualHEAD ? @"HEAD" : _request.method, _request.path, (unsigned long)_totalBytesRead, (unsigned long)_totalBytesWritten);
|
|
} else {
|
|
} else {
|
|
- GWS_LOG_VERBOSE(@"[%@] %@ %i \"(invalid request)\" (%lu | %lu)", self.localAddressString, self.remoteAddressString, (int)_statusCode, (unsigned long)_bytesRead, (unsigned long)_bytesWritten);
|
|
|
|
|
|
+ GWS_LOG_VERBOSE(@"[%@] %@ %i \"(invalid request)\" (%lu | %lu)", self.localAddressString, self.remoteAddressString, (int)_statusCode, (unsigned long)_totalBytesRead, (unsigned long)_totalBytesWritten);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|