2012-12-31 01:42:19 +08:00
|
|
|
/*
|
2014-02-07 10:44:04 +08:00
|
|
|
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
2012-12-31 01:42:19 +08:00
|
|
|
All rights reserved.
|
|
|
|
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
|
|
modification, are permitted provided that the following conditions are met:
|
|
|
|
* Redistributions of source code must retain the above copyright
|
|
|
|
notice, this list of conditions and the following disclaimer.
|
|
|
|
* Redistributions in binary form must reproduce the above copyright
|
|
|
|
notice, this list of conditions and the following disclaimer in the
|
|
|
|
documentation and/or other materials provided with the distribution.
|
2013-12-30 11:02:01 +08:00
|
|
|
* The name of Pierre-Olivier Latour may not be used to endorse
|
|
|
|
or promote products derived from this software without specific
|
|
|
|
prior written permission.
|
2012-12-31 01:42:19 +08:00
|
|
|
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
|
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
|
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
2013-12-30 11:02:01 +08:00
|
|
|
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
2012-12-31 01:42:19 +08:00
|
|
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
|
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
|
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
|
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
|
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
2014-03-30 12:17:51 +08:00
|
|
|
#import <netdb.h>
|
|
|
|
|
2012-12-31 01:42:19 +08:00
|
|
|
#import "GCDWebServerPrivate.h"
|
|
|
|
|
|
|
|
#define kHeadersReadBuffer 1024
|
|
|
|
|
|
|
|
typedef void (^ReadBufferCompletionBlock)(dispatch_data_t buffer);
|
|
|
|
typedef void (^ReadDataCompletionBlock)(NSData* data);
|
|
|
|
typedef void (^ReadHeadersCompletionBlock)(NSData* extraData);
|
|
|
|
typedef void (^ReadBodyCompletionBlock)(BOOL success);
|
|
|
|
|
|
|
|
typedef void (^WriteBufferCompletionBlock)(BOOL success);
|
|
|
|
typedef void (^WriteDataCompletionBlock)(BOOL success);
|
|
|
|
typedef void (^WriteHeadersCompletionBlock)(BOOL success);
|
|
|
|
typedef void (^WriteBodyCompletionBlock)(BOOL success);
|
|
|
|
|
|
|
|
static NSData* _separatorData = nil;
|
|
|
|
static NSData* _continueData = nil;
|
|
|
|
static NSDateFormatter* _dateFormatter = nil;
|
|
|
|
static dispatch_queue_t _formatterQueue = NULL;
|
|
|
|
|
2014-03-20 00:19:59 +08:00
|
|
|
@interface GCDWebServerConnection () {
|
|
|
|
@private
|
|
|
|
GCDWebServer* _server;
|
2014-03-30 12:17:51 +08:00
|
|
|
NSData* _localAddress;
|
|
|
|
NSData* _remoteAddress;
|
2014-03-20 00:19:59 +08:00
|
|
|
CFSocketNativeHandle _socket;
|
|
|
|
NSUInteger _bytesRead;
|
|
|
|
NSUInteger _bytesWritten;
|
|
|
|
|
|
|
|
CFHTTPMessageRef _requestMessage;
|
|
|
|
GCDWebServerRequest* _request;
|
|
|
|
GCDWebServerHandler* _handler;
|
|
|
|
CFHTTPMessageRef _responseMessage;
|
|
|
|
GCDWebServerResponse* _response;
|
|
|
|
}
|
|
|
|
@end
|
|
|
|
|
2012-12-31 01:42:19 +08:00
|
|
|
@implementation GCDWebServerConnection (Read)
|
|
|
|
|
2012-12-31 10:48:25 +08:00
|
|
|
- (void)_readBufferWithLength:(NSUInteger)length completionBlock:(ReadBufferCompletionBlock)block {
|
2013-04-02 06:42:16 +08:00
|
|
|
dispatch_read(_socket, length, kGCDWebServerGCDQueue, ^(dispatch_data_t buffer, int error) {
|
2012-12-31 01:42:19 +08:00
|
|
|
|
|
|
|
@autoreleasepool {
|
|
|
|
if (error == 0) {
|
|
|
|
size_t size = dispatch_data_get_size(buffer);
|
|
|
|
if (size > 0) {
|
2014-03-27 00:28:32 +08:00
|
|
|
LOG_DEBUG(@"Connection received %zu bytes on socket %i", size, _socket);
|
2012-12-31 01:42:19 +08:00
|
|
|
_bytesRead += size;
|
2014-03-20 00:44:15 +08:00
|
|
|
[self didUpdateBytesRead];
|
2012-12-31 01:42:19 +08:00
|
|
|
block(buffer);
|
|
|
|
} else {
|
|
|
|
if (_bytesRead > 0) {
|
|
|
|
LOG_ERROR(@"No more data available on socket %i", _socket);
|
|
|
|
} else {
|
|
|
|
LOG_WARNING(@"No data received from socket %i", _socket);
|
|
|
|
}
|
|
|
|
block(NULL);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
LOG_ERROR(@"Error while reading from socket %i: %s (%i)", _socket, strerror(error), error);
|
|
|
|
block(NULL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2012-12-31 10:48:25 +08:00
|
|
|
- (void)_readDataWithCompletionBlock:(ReadDataCompletionBlock)block {
|
2012-12-31 01:42:19 +08:00
|
|
|
[self _readBufferWithLength:SIZE_T_MAX completionBlock:^(dispatch_data_t buffer) {
|
|
|
|
|
|
|
|
if (buffer) {
|
|
|
|
NSMutableData* data = [[NSMutableData alloc] initWithCapacity:dispatch_data_get_size(buffer)];
|
2014-04-04 18:05:32 +08:00
|
|
|
dispatch_data_apply(buffer, ^bool(dispatch_data_t region, size_t offset, const void* bufferChunk, size_t size) {
|
|
|
|
[data appendBytes:bufferChunk length:size];
|
2012-12-31 01:42:19 +08:00
|
|
|
return true;
|
|
|
|
});
|
|
|
|
block(data);
|
2014-01-09 14:27:15 +08:00
|
|
|
ARC_RELEASE(data);
|
2012-12-31 01:42:19 +08:00
|
|
|
} else {
|
|
|
|
block(nil);
|
|
|
|
}
|
|
|
|
|
|
|
|
}];
|
|
|
|
}
|
|
|
|
|
2012-12-31 10:48:25 +08:00
|
|
|
- (void)_readHeadersWithCompletionBlock:(ReadHeadersCompletionBlock)block {
|
2012-12-31 01:42:19 +08:00
|
|
|
DCHECK(_requestMessage);
|
|
|
|
[self _readBufferWithLength:SIZE_T_MAX completionBlock:^(dispatch_data_t buffer) {
|
|
|
|
|
|
|
|
if (buffer) {
|
2013-03-29 04:09:24 +08:00
|
|
|
NSMutableData* data = [NSMutableData dataWithCapacity:kHeadersReadBuffer];
|
2014-04-04 18:05:32 +08:00
|
|
|
dispatch_data_apply(buffer, ^bool(dispatch_data_t region, size_t offset, const void* bufferChunk, size_t size) {
|
|
|
|
[data appendBytes:bufferChunk length:size];
|
2012-12-31 01:42:19 +08:00
|
|
|
return true;
|
|
|
|
});
|
|
|
|
NSRange range = [data rangeOfData:_separatorData options:0 range:NSMakeRange(0, data.length)];
|
|
|
|
if (range.location == NSNotFound) {
|
2013-03-29 04:09:24 +08:00
|
|
|
if (CFHTTPMessageAppendBytes(_requestMessage, data.bytes, data.length)) {
|
|
|
|
[self _readHeadersWithCompletionBlock:block];
|
|
|
|
} else {
|
|
|
|
LOG_ERROR(@"Failed appending request headers data from socket %i", _socket);
|
|
|
|
block(nil);
|
|
|
|
}
|
2012-12-31 01:42:19 +08:00
|
|
|
} else {
|
|
|
|
NSUInteger length = range.location + range.length;
|
|
|
|
if (CFHTTPMessageAppendBytes(_requestMessage, data.bytes, length)) {
|
|
|
|
if (CFHTTPMessageIsHeaderComplete(_requestMessage)) {
|
|
|
|
block([data subdataWithRange:NSMakeRange(length, data.length - length)]);
|
|
|
|
} else {
|
|
|
|
LOG_ERROR(@"Failed parsing request headers from socket %i", _socket);
|
|
|
|
block(nil);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
LOG_ERROR(@"Failed appending request headers data from socket %i", _socket);
|
|
|
|
block(nil);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
block(nil);
|
|
|
|
}
|
|
|
|
|
|
|
|
}];
|
|
|
|
}
|
|
|
|
|
2012-12-31 10:48:25 +08:00
|
|
|
- (void)_readBodyWithRemainingLength:(NSUInteger)length completionBlock:(ReadBodyCompletionBlock)block {
|
2012-12-31 01:42:19 +08:00
|
|
|
DCHECK([_request hasBody]);
|
|
|
|
[self _readBufferWithLength:length completionBlock:^(dispatch_data_t buffer) {
|
|
|
|
|
|
|
|
if (buffer) {
|
|
|
|
NSInteger remainingLength = length - dispatch_data_get_size(buffer);
|
|
|
|
if (remainingLength >= 0) {
|
2014-04-04 18:05:32 +08:00
|
|
|
bool success = dispatch_data_apply(buffer, ^bool(dispatch_data_t region, size_t offset, const void* bufferChunk, size_t size) {
|
|
|
|
NSInteger result = [_request write:bufferChunk maxLength:size];
|
2014-03-20 23:56:54 +08:00
|
|
|
if (result != (NSInteger)size) {
|
2012-12-31 01:42:19 +08:00
|
|
|
LOG_ERROR(@"Failed writing request body on socket %i (error %i)", _socket, (int)result);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
});
|
|
|
|
if (success) {
|
|
|
|
if (remainingLength > 0) {
|
|
|
|
[self _readBodyWithRemainingLength:remainingLength completionBlock:block];
|
|
|
|
} else {
|
|
|
|
block(YES);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
block(NO);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
block(NO);
|
2014-03-20 23:56:54 +08:00
|
|
|
DNOT_REACHED();
|
2012-12-31 01:42:19 +08:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
block(NO);
|
|
|
|
}
|
|
|
|
|
|
|
|
}];
|
|
|
|
}
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation GCDWebServerConnection (Write)
|
|
|
|
|
2012-12-31 10:48:25 +08:00
|
|
|
- (void)_writeBuffer:(dispatch_data_t)buffer withCompletionBlock:(WriteBufferCompletionBlock)block {
|
2012-12-31 01:42:19 +08:00
|
|
|
size_t size = dispatch_data_get_size(buffer);
|
2013-04-02 06:42:16 +08:00
|
|
|
dispatch_write(_socket, buffer, kGCDWebServerGCDQueue, ^(dispatch_data_t data, int error) {
|
2012-12-31 01:42:19 +08:00
|
|
|
|
|
|
|
@autoreleasepool {
|
|
|
|
if (error == 0) {
|
|
|
|
DCHECK(data == NULL);
|
2014-03-27 00:28:32 +08:00
|
|
|
LOG_DEBUG(@"Connection sent %zu bytes on socket %i", size, _socket);
|
2012-12-31 01:42:19 +08:00
|
|
|
_bytesWritten += size;
|
2014-03-20 00:44:15 +08:00
|
|
|
[self didUpdateBytesWritten];
|
2012-12-31 01:42:19 +08:00
|
|
|
block(YES);
|
|
|
|
} else {
|
|
|
|
LOG_ERROR(@"Error while writing to socket %i: %s (%i)", _socket, strerror(error), error);
|
|
|
|
block(NO);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2012-12-31 10:48:25 +08:00
|
|
|
- (void)_writeData:(NSData*)data withCompletionBlock:(WriteDataCompletionBlock)block {
|
2014-01-09 14:27:15 +08:00
|
|
|
#if !__has_feature(objc_arc)
|
2012-12-31 01:42:19 +08:00
|
|
|
[data retain];
|
2014-01-09 14:27:15 +08:00
|
|
|
#endif
|
2014-01-24 03:20:43 +08:00
|
|
|
dispatch_data_t buffer = dispatch_data_create(data.bytes, data.length, dispatch_get_main_queue(), ^{
|
2014-01-09 14:27:15 +08:00
|
|
|
#if __has_feature(objc_arc)
|
|
|
|
[data self]; // Keeps ARC from releasing data too early
|
|
|
|
#else
|
2012-12-31 01:42:19 +08:00
|
|
|
[data release];
|
2014-01-09 14:27:15 +08:00
|
|
|
#endif
|
2012-12-31 01:42:19 +08:00
|
|
|
});
|
|
|
|
[self _writeBuffer:buffer withCompletionBlock:block];
|
2014-01-24 03:33:33 +08:00
|
|
|
ARC_DISPATCH_RELEASE(buffer);
|
2012-12-31 01:42:19 +08:00
|
|
|
}
|
|
|
|
|
2012-12-31 10:48:25 +08:00
|
|
|
- (void)_writeHeadersWithCompletionBlock:(WriteHeadersCompletionBlock)block {
|
2012-12-31 01:42:19 +08:00
|
|
|
DCHECK(_responseMessage);
|
|
|
|
CFDataRef message = CFHTTPMessageCopySerializedMessage(_responseMessage);
|
2014-01-09 14:27:15 +08:00
|
|
|
[self _writeData:(ARC_BRIDGE NSData*)message withCompletionBlock:block];
|
2012-12-31 01:42:19 +08:00
|
|
|
CFRelease(message);
|
|
|
|
}
|
|
|
|
|
2012-12-31 10:48:25 +08:00
|
|
|
- (void)_writeBodyWithCompletionBlock:(WriteBodyCompletionBlock)block {
|
2012-12-31 01:42:19 +08:00
|
|
|
DCHECK([_response hasBody]);
|
2014-04-07 01:44:53 +08:00
|
|
|
NSError* error = nil;
|
|
|
|
NSData* data = [_response performReadData:&error];
|
|
|
|
if (data) {
|
|
|
|
if (data.length) {
|
|
|
|
[self _writeData:data withCompletionBlock:^(BOOL success) {
|
|
|
|
|
|
|
|
if (success) {
|
|
|
|
[self _writeBodyWithCompletionBlock:block];
|
|
|
|
} else {
|
|
|
|
block(NO);
|
|
|
|
}
|
|
|
|
|
|
|
|
}];
|
|
|
|
} else {
|
|
|
|
block(YES);
|
|
|
|
}
|
2012-12-31 01:42:19 +08:00
|
|
|
} else {
|
2014-04-07 01:44:53 +08:00
|
|
|
LOG_ERROR(@"Failed reading response body for socket %i: %@", _socket, error);
|
|
|
|
block(NO);
|
2012-12-31 01:42:19 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation GCDWebServerConnection
|
|
|
|
|
2014-03-30 12:17:51 +08:00
|
|
|
@synthesize server=_server, localAddressData=_localAddress, remoteAddressData=_remoteAddress, totalBytesRead=_bytesRead, totalBytesWritten=_bytesWritten;
|
2012-12-31 01:42:19 +08:00
|
|
|
|
2012-12-31 10:48:25 +08:00
|
|
|
+ (void)initialize {
|
2012-12-31 01:42:19 +08:00
|
|
|
if (_separatorData == nil) {
|
|
|
|
_separatorData = [[NSData alloc] initWithBytes:"\r\n\r\n" length:4];
|
|
|
|
DCHECK(_separatorData);
|
|
|
|
}
|
|
|
|
if (_continueData == nil) {
|
|
|
|
CFHTTPMessageRef message = CFHTTPMessageCreateResponse(kCFAllocatorDefault, 100, NULL, kCFHTTPVersion1_1);
|
2014-01-09 14:27:15 +08:00
|
|
|
#if __has_feature(objc_arc)
|
|
|
|
_continueData = CFBridgingRelease(CFHTTPMessageCopySerializedMessage(message));
|
|
|
|
#else
|
2012-12-31 01:42:19 +08:00
|
|
|
_continueData = (NSData*)CFHTTPMessageCopySerializedMessage(message);
|
2014-01-09 14:27:15 +08:00
|
|
|
#endif
|
2012-12-31 01:42:19 +08:00
|
|
|
CFRelease(message);
|
|
|
|
DCHECK(_continueData);
|
|
|
|
}
|
|
|
|
if (_dateFormatter == nil) {
|
2014-01-24 03:44:43 +08:00
|
|
|
DCHECK([NSThread isMainThread]); // NSDateFormatter should be initialized on main thread
|
2012-12-31 01:42:19 +08:00
|
|
|
_dateFormatter = [[NSDateFormatter alloc] init];
|
|
|
|
_dateFormatter.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"GMT"];
|
|
|
|
_dateFormatter.dateFormat = @"EEE',' dd MMM yyyy HH':'mm':'ss 'GMT'";
|
2014-01-09 14:27:15 +08:00
|
|
|
_dateFormatter.locale = ARC_AUTORELEASE([[NSLocale alloc] initWithLocaleIdentifier:@"en_US"]);
|
2012-12-31 01:42:19 +08:00
|
|
|
DCHECK(_dateFormatter);
|
|
|
|
}
|
|
|
|
if (_formatterQueue == NULL) {
|
|
|
|
_formatterQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);
|
|
|
|
DCHECK(_formatterQueue);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-12-31 10:48:25 +08:00
|
|
|
- (void)_initializeResponseHeadersWithStatusCode:(NSInteger)statusCode {
|
2012-12-31 01:42:19 +08:00
|
|
|
_responseMessage = CFHTTPMessageCreateResponse(kCFAllocatorDefault, statusCode, NULL, kCFHTTPVersion1_1);
|
|
|
|
CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Connection"), CFSTR("Close"));
|
2014-01-09 14:27:15 +08:00
|
|
|
CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Server"), (ARC_BRIDGE CFStringRef)[[_server class] serverName]);
|
2012-12-31 01:42:19 +08:00
|
|
|
dispatch_sync(_formatterQueue, ^{
|
|
|
|
NSString* date = [_dateFormatter stringFromDate:[NSDate date]];
|
2014-01-09 14:27:15 +08:00
|
|
|
CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Date"), (ARC_BRIDGE CFStringRef)date);
|
2012-12-31 01:42:19 +08:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2012-12-31 10:48:25 +08:00
|
|
|
- (void)_abortWithStatusCode:(NSUInteger)statusCode {
|
2012-12-31 01:42:19 +08:00
|
|
|
DCHECK(_responseMessage == NULL);
|
|
|
|
DCHECK((statusCode >= 400) && (statusCode < 600));
|
|
|
|
[self _initializeResponseHeadersWithStatusCode:statusCode];
|
|
|
|
[self _writeHeadersWithCompletionBlock:^(BOOL success) {
|
|
|
|
; // Nothing more to do
|
|
|
|
}];
|
2014-03-27 00:28:32 +08:00
|
|
|
LOG_DEBUG(@"Connection aborted with status code %i on socket %i", (int)statusCode, _socket);
|
2012-12-31 01:42:19 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
|
2012-12-31 10:48:25 +08:00
|
|
|
- (void)_processRequest {
|
2012-12-31 01:42:19 +08:00
|
|
|
DCHECK(_responseMessage == NULL);
|
|
|
|
|
|
|
|
GCDWebServerResponse* response = [self processRequest:_request withBlock:_handler.processBlock];
|
2014-04-07 01:44:53 +08:00
|
|
|
if (response) {
|
|
|
|
NSError* error = nil;
|
|
|
|
if ([response hasBody] && ![response performOpen:&error]) {
|
|
|
|
LOG_WARNING(@"Failed opening response body for socket %i: %@", _socket, error);
|
|
|
|
} else {
|
|
|
|
_response = ARC_RETAIN(response);
|
|
|
|
}
|
2012-12-31 01:42:19 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (_response) {
|
|
|
|
[self _initializeResponseHeadersWithStatusCode:_response.statusCode];
|
2014-04-04 06:23:33 +08:00
|
|
|
if (_response.cacheControlMaxAge > 0) {
|
|
|
|
CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Cache-Control"), (ARC_BRIDGE CFStringRef)[NSString stringWithFormat:@"max-age=%i, public", (int)_response.cacheControlMaxAge]);
|
2012-12-31 01:42:19 +08:00
|
|
|
} else {
|
|
|
|
CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Cache-Control"), CFSTR("no-cache"));
|
|
|
|
}
|
2014-04-04 06:23:33 +08:00
|
|
|
if (_response.contentType != nil) {
|
|
|
|
CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Content-Type"), (ARC_BRIDGE CFStringRef)_response.contentType);
|
|
|
|
}
|
|
|
|
if (_response.contentLength != NSNotFound) {
|
|
|
|
CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Content-Length"), (ARC_BRIDGE CFStringRef)[NSString stringWithFormat:@"%lu", (unsigned long)_response.contentLength]);
|
|
|
|
}
|
2012-12-31 01:42:19 +08:00
|
|
|
[_response.additionalHeaders enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL* stop) {
|
2014-01-09 14:27:15 +08:00
|
|
|
CFHTTPMessageSetHeaderFieldValue(_responseMessage, (ARC_BRIDGE CFStringRef)key, (ARC_BRIDGE CFStringRef)obj);
|
2012-12-31 01:42:19 +08:00
|
|
|
}];
|
|
|
|
[self _writeHeadersWithCompletionBlock:^(BOOL success) {
|
|
|
|
|
|
|
|
if (success) {
|
|
|
|
if ([_response hasBody]) {
|
2014-04-04 18:05:32 +08:00
|
|
|
[self _writeBodyWithCompletionBlock:^(BOOL successInner) {
|
2012-12-31 01:42:19 +08:00
|
|
|
|
2014-04-07 01:44:53 +08:00
|
|
|
[_response performClose];
|
|
|
|
LOG_VERBOSE(@"%@ | %@ \"%@ %@\" %i %lu", self.localAddressString, self.remoteAddressString, _request.method, _request.path, (int)_response.statusCode, (unsigned long)_bytesWritten);
|
2012-12-31 01:42:19 +08:00
|
|
|
|
|
|
|
}];
|
|
|
|
}
|
|
|
|
} else if ([_response hasBody]) {
|
2014-04-07 01:44:53 +08:00
|
|
|
[_response performClose];
|
2012-12-31 01:42:19 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
}];
|
|
|
|
} else {
|
|
|
|
[self _abortWithStatusCode:500];
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2012-12-31 10:48:25 +08:00
|
|
|
- (void)_readRequestBody:(NSData*)initialData {
|
2012-12-31 01:42:19 +08:00
|
|
|
if ([_request open]) {
|
|
|
|
NSInteger length = _request.contentLength;
|
|
|
|
if (initialData.length) {
|
|
|
|
NSInteger result = [_request write:initialData.bytes maxLength:initialData.length];
|
2014-03-20 23:56:54 +08:00
|
|
|
if (result == (NSInteger)initialData.length) {
|
2012-12-31 01:42:19 +08:00
|
|
|
length -= initialData.length;
|
|
|
|
DCHECK(length >= 0);
|
|
|
|
} else {
|
|
|
|
LOG_ERROR(@"Failed writing request body on socket %i (error %i)", _socket, (int)result);
|
|
|
|
length = -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (length > 0) {
|
|
|
|
[self _readBodyWithRemainingLength:length completionBlock:^(BOOL success) {
|
|
|
|
|
|
|
|
if (![_request close]) {
|
|
|
|
success = NO;
|
|
|
|
}
|
|
|
|
if (success) {
|
|
|
|
[self _processRequest];
|
|
|
|
} else {
|
|
|
|
[self _abortWithStatusCode:500];
|
|
|
|
}
|
|
|
|
|
|
|
|
}];
|
|
|
|
} else if (length == 0) {
|
|
|
|
if ([_request close]) {
|
|
|
|
[self _processRequest];
|
|
|
|
} else {
|
|
|
|
[self _abortWithStatusCode:500];
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
[_request close]; // Can't do anything with result anyway
|
|
|
|
[self _abortWithStatusCode:500];
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
[self _abortWithStatusCode:500];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-12-31 10:48:25 +08:00
|
|
|
- (void)_readRequestHeaders {
|
2012-12-31 01:42:19 +08:00
|
|
|
_requestMessage = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, true);
|
|
|
|
[self _readHeadersWithCompletionBlock:^(NSData* extraData) {
|
|
|
|
|
|
|
|
if (extraData) {
|
2014-01-09 14:27:15 +08:00
|
|
|
NSString* requestMethod = [ARC_BRIDGE_RELEASE(CFHTTPMessageCopyRequestMethod(_requestMessage)) uppercaseString];
|
2012-12-31 01:42:19 +08:00
|
|
|
DCHECK(requestMethod);
|
2014-01-09 14:27:15 +08:00
|
|
|
NSURL* requestURL = ARC_BRIDGE_RELEASE(CFHTTPMessageCopyRequestURL(_requestMessage));
|
2012-12-31 01:42:19 +08:00
|
|
|
DCHECK(requestURL);
|
2014-01-09 14:27:15 +08:00
|
|
|
NSString* requestPath = GCDWebServerUnescapeURLString(ARC_BRIDGE_RELEASE(CFURLCopyPath((CFURLRef)requestURL))); // Don't use -[NSURL path] which strips the ending slash
|
2012-12-31 01:42:19 +08:00
|
|
|
DCHECK(requestPath);
|
|
|
|
NSDictionary* requestQuery = nil;
|
2014-01-09 14:27:15 +08:00
|
|
|
NSString* queryString = ARC_BRIDGE_RELEASE(CFURLCopyQueryString((CFURLRef)requestURL, NULL)); // Don't use -[NSURL query] to make sure query is not unescaped;
|
2012-12-31 01:42:19 +08:00
|
|
|
if (queryString.length) {
|
|
|
|
requestQuery = GCDWebServerParseURLEncodedForm(queryString);
|
|
|
|
DCHECK(requestQuery);
|
|
|
|
}
|
2014-01-09 14:27:15 +08:00
|
|
|
NSDictionary* requestHeaders = ARC_BRIDGE_RELEASE(CFHTTPMessageCopyAllHeaderFields(_requestMessage));
|
2012-12-31 01:42:19 +08:00
|
|
|
DCHECK(requestHeaders);
|
|
|
|
for (_handler in _server.handlers) {
|
2014-01-09 14:27:15 +08:00
|
|
|
_request = ARC_RETAIN(_handler.matchBlock(requestMethod, requestURL, requestHeaders, requestPath, requestQuery));
|
2012-12-31 01:42:19 +08:00
|
|
|
if (_request) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (_request) {
|
|
|
|
if (_request.hasBody) {
|
|
|
|
if (extraData.length <= _request.contentLength) {
|
2014-01-09 14:27:15 +08:00
|
|
|
NSString* expectHeader = ARC_BRIDGE_RELEASE(CFHTTPMessageCopyHeaderFieldValue(_requestMessage, CFSTR("Expect")));
|
2012-12-31 01:42:19 +08:00
|
|
|
if (expectHeader) {
|
|
|
|
if ([expectHeader caseInsensitiveCompare:@"100-continue"] == NSOrderedSame) {
|
|
|
|
[self _writeData:_continueData withCompletionBlock:^(BOOL success) {
|
|
|
|
|
|
|
|
if (success) {
|
|
|
|
[self _readRequestBody:extraData];
|
|
|
|
}
|
|
|
|
|
|
|
|
}];
|
|
|
|
} else {
|
|
|
|
LOG_ERROR(@"Unsupported 'Expect' / 'Content-Length' header combination on socket %i", _socket);
|
|
|
|
[self _abortWithStatusCode:417];
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
[self _readRequestBody:extraData];
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
LOG_ERROR(@"Unexpected 'Content-Length' header value on socket %i", _socket);
|
|
|
|
[self _abortWithStatusCode:400];
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
[self _processRequest];
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
[self _abortWithStatusCode:405];
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
[self _abortWithStatusCode:500];
|
|
|
|
}
|
|
|
|
|
|
|
|
}];
|
|
|
|
}
|
|
|
|
|
2014-03-30 12:17:51 +08:00
|
|
|
- (id)initWithServer:(GCDWebServer*)server localAddress:(NSData*)localAddress remoteAddress:(NSData*)remoteAddress socket:(CFSocketNativeHandle)socket {
|
2012-12-31 01:42:19 +08:00
|
|
|
if ((self = [super init])) {
|
2014-01-09 14:27:15 +08:00
|
|
|
_server = ARC_RETAIN(server);
|
2014-03-30 12:17:51 +08:00
|
|
|
_localAddress = ARC_RETAIN(localAddress);
|
|
|
|
_remoteAddress = ARC_RETAIN(remoteAddress);
|
2012-12-31 01:42:19 +08:00
|
|
|
_socket = socket;
|
|
|
|
|
|
|
|
[self open];
|
|
|
|
}
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
2014-03-30 12:17:51 +08:00
|
|
|
static NSString* _StringFromAddressData(NSData* data) {
|
|
|
|
NSString* string = nil;
|
|
|
|
const struct sockaddr* addr = data.bytes;
|
|
|
|
char hostBuffer[NI_MAXHOST];
|
|
|
|
char serviceBuffer[NI_MAXSERV];
|
|
|
|
if (getnameinfo(addr, addr->sa_len, hostBuffer, sizeof(hostBuffer), serviceBuffer, sizeof(serviceBuffer), NI_NUMERICHOST | NI_NUMERICSERV | NI_NOFQDN) >= 0) {
|
|
|
|
string = [NSString stringWithFormat:@"%s:%s", hostBuffer, serviceBuffer];
|
|
|
|
} else {
|
|
|
|
DNOT_REACHED();
|
|
|
|
}
|
|
|
|
return string;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString*)localAddressString {
|
|
|
|
return _StringFromAddressData(_localAddress);
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString*)remoteAddressString {
|
|
|
|
return _StringFromAddressData(_remoteAddress);
|
|
|
|
}
|
|
|
|
|
2012-12-31 10:48:25 +08:00
|
|
|
- (void)dealloc {
|
2012-12-31 01:42:19 +08:00
|
|
|
[self close];
|
|
|
|
|
2014-01-09 14:27:15 +08:00
|
|
|
ARC_RELEASE(_server);
|
2014-03-30 12:17:51 +08:00
|
|
|
ARC_RELEASE(_localAddress);
|
|
|
|
ARC_RELEASE(_remoteAddress);
|
2012-12-31 01:42:19 +08:00
|
|
|
|
|
|
|
if (_requestMessage) {
|
|
|
|
CFRelease(_requestMessage);
|
|
|
|
}
|
2014-01-09 14:27:15 +08:00
|
|
|
ARC_RELEASE(_request);
|
2012-12-31 01:42:19 +08:00
|
|
|
|
|
|
|
if (_responseMessage) {
|
|
|
|
CFRelease(_responseMessage);
|
|
|
|
}
|
2014-01-09 14:27:15 +08:00
|
|
|
ARC_RELEASE(_response);
|
2012-12-31 01:42:19 +08:00
|
|
|
|
2014-01-09 14:27:15 +08:00
|
|
|
ARC_DEALLOC(super);
|
2012-12-31 01:42:19 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation GCDWebServerConnection (Subclassing)
|
|
|
|
|
2012-12-31 10:48:25 +08:00
|
|
|
- (void)open {
|
2012-12-31 01:42:19 +08:00
|
|
|
LOG_DEBUG(@"Did open connection on socket %i", _socket);
|
|
|
|
[self _readRequestHeaders];
|
|
|
|
}
|
|
|
|
|
2014-03-20 00:44:15 +08:00
|
|
|
- (void)didUpdateBytesRead {
|
|
|
|
;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)didUpdateBytesWritten {
|
|
|
|
;
|
|
|
|
}
|
|
|
|
|
2012-12-31 10:48:25 +08:00
|
|
|
- (GCDWebServerResponse*)processRequest:(GCDWebServerRequest*)request withBlock:(GCDWebServerProcessBlock)block {
|
2014-03-28 02:37:53 +08:00
|
|
|
LOG_DEBUG(@"Connection on socket %i processing %@ request for \"%@\" (%lu bytes body)", _socket, _request.method, _request.path, (unsigned long)_request.contentLength);
|
2012-12-31 01:42:19 +08:00
|
|
|
GCDWebServerResponse* response = nil;
|
|
|
|
@try {
|
|
|
|
response = block(request);
|
|
|
|
}
|
|
|
|
@catch (NSException* exception) {
|
|
|
|
LOG_EXCEPTION(exception);
|
|
|
|
}
|
|
|
|
return response;
|
|
|
|
}
|
|
|
|
|
2012-12-31 10:48:25 +08:00
|
|
|
- (void)close {
|
2013-04-02 06:42:16 +08:00
|
|
|
int result = close(_socket);
|
|
|
|
if (result != 0) {
|
|
|
|
LOG_ERROR(@"Failed closing socket %i for connection (%i): %s", _socket, errno, strerror(errno));
|
|
|
|
}
|
2012-12-31 01:42:19 +08:00
|
|
|
LOG_DEBUG(@"Did close connection on socket %i", _socket);
|
|
|
|
}
|
|
|
|
|
|
|
|
@end
|