Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bonjour: nullify delegate before removing service from current runloop to prevent sending messages to deallocated instance #81

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
Open
23 changes: 23 additions & 0 deletions CocoaHTTPServer.podspec
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
Pod::Spec.new do |s|
s.name = 'CocoaHTTPServer'
s.version = '2.4.1'
s.license = 'BSD'
s.summary = 'A small, lightweight, embeddable HTTP server for Mac OS X or iOS applications.'
s.homepage = 'https://github.com/gpinigin/CocoaHTTPServer'
s.authors = { 'Gleb Pinigin' => '[email protected]', 'Robbie Hanson' => '[email protected]' }
s.source = { :git => 'https://github.com/gpinigin/CocoaHTTPServer.git', :tag => s.version.to_s }
s.source_files = '{Core,Extensions}/**/*.{h,m}'
s.requires_arc = true

s.ios.deployment_target = '5.0'
s.osx.deployment_target = '10.7'

s.ios.frameworks = 'CFNetwork', 'Security', 'MobileCoreServices'
s.osx.frameworks = 'CoreServices', 'Security', 'MobileCoreServices'

s.library = 'xml2'
s.xcconfig = { 'HEADER_SEARCH_PATHS' => '"$(SDKROOT)/usr/include/libxml2"' }

s.dependency "CocoaAsyncSocket"
s.dependency "CocoaLumberjack"
end
2 changes: 1 addition & 1 deletion Core/HTTPConnection.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@

NSObject<HTTPResponse> *httpResponse;

NSMutableArray *ranges;
NSArray *ranges;
NSMutableArray *ranges_headers;
NSString *ranges_boundry;
int rangeIndex;
Expand Down
166 changes: 90 additions & 76 deletions Core/HTTPConnection.m
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
#import "HTTPFileResponse.h"
#import "HTTPAsyncFileResponse.h"
#import "WebSocket.h"

#import "HTTPLogging.h"
#import <MobileCoreServices/MobileCoreServices.h>

#if ! __has_feature(objc_arc)
#warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
Expand Down Expand Up @@ -204,7 +206,7 @@ - (id)initWithAsyncSocket:(GCDAsyncSocket *)newSocket configuration:(HTTPConfig
lastNC = 0;

// Create a new HTTP message
request = [[HTTPMessage alloc] initEmptyRequest];
request = [[HTTPMessage alloc] init];

numHeaderLines = 0;

Expand Down Expand Up @@ -732,7 +734,7 @@ - (NSDictionary *)parseGetParams
* If successfull, the variables 'ranges' and 'rangeIndex' will be updated, and YES will be returned.
* Otherwise, NO is returned, and the range request should be ignored.
**/
- (BOOL)parseRangeRequest:(NSString *)rangeHeader withContentLength:(UInt64)contentLength
- (NSArray *)parseRangeRequest:(NSString *)rangeHeader withContentLength:(UInt64)contentLength
{
HTTPLogTrace();

Expand All @@ -755,27 +757,28 @@ - (BOOL)parseRangeRequest:(NSString *)rangeHeader withContentLength:(UInt64)cont

NSRange eqsignRange = [rangeHeader rangeOfString:@"="];

if(eqsignRange.location == NSNotFound) return NO;
if(eqsignRange.location == NSNotFound)
return nil;

NSUInteger tIndex = eqsignRange.location;
NSUInteger fIndex = eqsignRange.location + eqsignRange.length;

NSMutableString *rangeType = [[rangeHeader substringToIndex:tIndex] mutableCopy];
NSMutableString *rangeValue = [[rangeHeader substringFromIndex:fIndex] mutableCopy];

CFStringTrimWhitespace((__bridge CFMutableStringRef)rangeType);
CFStringTrimWhitespace((__bridge CFMutableStringRef)rangeValue);

if([rangeType caseInsensitiveCompare:@"bytes"] != NSOrderedSame) return NO;
CFStringTrimWhitespace((__bridge CFMutableStringRef)rangeType);

if([rangeType caseInsensitiveCompare:@"bytes"] != NSOrderedSame)
return nil;

NSMutableString *rangeValue = [[rangeHeader substringFromIndex:fIndex] mutableCopy];
CFStringTrimWhitespace((__bridge CFMutableStringRef)rangeValue);

NSArray *rangeComponents = [rangeValue componentsSeparatedByString:@","];
if([rangeComponents count] == 0)
return nil;

if([rangeComponents count] == 0) return NO;

ranges = [[NSMutableArray alloc] initWithCapacity:[rangeComponents count]];

rangeIndex = 0;
NSMutableArray *headerRanges = [[NSMutableArray alloc] initWithCapacity:rangeComponents.count];


// Note: We store all range values in the form of DDRange structs, wrapped in NSValue objects.
// Since DDRange consists of UInt64 values, the range extends up to 16 exabytes.

Expand All @@ -791,11 +794,13 @@ - (BOOL)parseRangeRequest:(NSString *)rangeHeader withContentLength:(UInt64)cont
// We're dealing with an individual byte number

UInt64 byteIndex;
if(![NSNumber parseString:rangeComponent intoUInt64:&byteIndex]) return NO;
if(![NSNumber parseString:rangeComponent intoUInt64:&byteIndex])
return nil;

if(byteIndex >= contentLength) return NO;
if(byteIndex >= contentLength)
return nil;

[ranges addObject:[NSValue valueWithDDRange:DDMakeRange(byteIndex, 1)]];
[headerRanges addObject:[NSValue valueWithDDRange:DDMakeRange(byteIndex, 1)]];
}
else
{
Expand All @@ -818,38 +823,46 @@ - (BOOL)parseRangeRequest:(NSString *)rangeHeader withContentLength:(UInt64)cont
//
// r2 is the number of ending bytes to include in the range

if(!hasR2) return NO;
if(r2 > contentLength) return NO;
if (!hasR2)
return nil;

if (r2 > contentLength)
return nil;

UInt64 startIndex = contentLength - r2;

[ranges addObject:[NSValue valueWithDDRange:DDMakeRange(startIndex, r2)]];
[headerRanges addObject:[NSValue valueWithDDRange:DDMakeRange(startIndex, r2)]];
}
else if (!hasR2)
{
// We're dealing with a "[#]-" range
//
// r1 is the starting index of the range, which goes all the way to the end

if(r1 >= contentLength) return NO;
if(r1 >= contentLength)
return nil;

[ranges addObject:[NSValue valueWithDDRange:DDMakeRange(r1, contentLength - r1)]];
[headerRanges addObject:[NSValue valueWithDDRange:DDMakeRange(r1, contentLength - r1)]];
}
else
{
// We're dealing with a normal "[#]-[#]" range
//
// Note: The range is inclusive. So 0-1 has a length of 2 bytes.

if(r1 > r2) return NO;
if(r2 >= contentLength) return NO;
if(r1 > r2)
return nil;

if(r2 >= contentLength)
return nil;

[ranges addObject:[NSValue valueWithDDRange:DDMakeRange(r1, r2 - r1 + 1)]];
[headerRanges addObject:[NSValue valueWithDDRange:DDMakeRange(r1, r2 - r1 + 1)]];
}
}
}

if([ranges count] == 0) return NO;
if([ranges count] == 0)
return nil;

// Now make sure none of the ranges overlap

Expand All @@ -865,17 +878,14 @@ - (BOOL)parseRangeRequest:(NSString *)rangeHeader withContentLength:(UInt64)cont
DDRange iRange = DDIntersectionRange(range1, range2);

if(iRange.length != 0)
{
return NO;
}
return nil;
}
}

// Sort the ranges
[headerRanges sortUsingSelector:@selector(ddrangeCompare:)];

[ranges sortUsingSelector:@selector(ddrangeCompare:)];

return YES;
return [headerRanges copy];
}

- (NSString *)requestURI
Expand Down Expand Up @@ -1158,8 +1168,10 @@ - (void)sendResponseHeadersAndBody

if (!isChunked && rangeHeader)
{
if ([self parseRangeRequest:rangeHeader withContentLength:contentLength])
ranges = [self parseRangeRequest:rangeHeader withContentLength:contentLength];
if (ranges)
{
rangeIndex = 0;
isRangeRequest = YES;
}
}
Expand Down Expand Up @@ -1911,6 +1923,27 @@ - (NSString *)dateAsString:(NSDate *)date
return [df stringFromDate:date];
}

#pragma mark - HTTP headers

- (NSString *)fileMIMEType:(NSString *)extension
{
CFStringRef UTI = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension,
(__bridge CFStringRef)extension,
NULL);
CFStringRef MIMEType = UTTypeCopyPreferredTagWithClass (UTI, kUTTagClassMIMEType);
CFRelease(UTI);
return CFBridgingRelease(MIMEType);
}

- (void)setupStandardHeaders:(HTTPMessage *)response {
// Add standard headers
NSString *now = [self dateAsString:[NSDate date]];
[response setHeaderField:@"Date" value:now];

// Add server capability headers
[response setHeaderField:@"Accept-Ranges" value:@"bytes"];
}

/**
* This method is called immediately prior to sending the response headers.
* This method adds standard header fields, and then converts the response to an NSData object.
Expand All @@ -1922,29 +1955,21 @@ - (NSData *)preprocessResponse:(HTTPMessage *)response
// Override me to customize the response headers
// You'll likely want to add your own custom headers, and then return [super preprocessResponse:response]

// Add standard headers
NSString *now = [self dateAsString:[NSDate date]];
[response setHeaderField:@"Date" value:now];

// Add server capability headers
[response setHeaderField:@"Accept-Ranges" value:@"bytes"];

// Add optional response headers
if ([httpResponse respondsToSelector:@selector(httpHeaders)])
{
[self setupStandardHeaders:response];
NSString *mimeType = [self fileMIMEType:request.url.pathExtension];
[response setHeaderField:@"Content-Type" value:mimeType];

// Add optional response headers
if ([httpResponse respondsToSelector:@selector(httpHeaders)]) {
NSDictionary *responseHeaders = [httpResponse httpHeaders];

NSEnumerator *keyEnumerator = [responseHeaders keyEnumerator];
NSString *key;

while ((key = [keyEnumerator nextObject]))
{
NSString *value = [responseHeaders objectForKey:key];

[response setHeaderField:key value:value];
}

NSArray *allKeys = [responseHeaders allKeys];
for (NSString *key in allKeys) {
NSString *value = [responseHeaders objectForKey:key];
[response setHeaderField:key value:value];
}
}

return [response messageData];
}

Expand Down Expand Up @@ -1975,29 +2000,18 @@ - (NSData *)preprocessErrorResponse:(HTTPMessage *)response
// [response setHeaderField:@"Content-Length" value:contentLengthStr];
// }

// Add standard headers
NSString *now = [self dateAsString:[NSDate date]];
[response setHeaderField:@"Date" value:now];

// Add server capability headers
[response setHeaderField:@"Accept-Ranges" value:@"bytes"];

// Add optional response headers
if ([httpResponse respondsToSelector:@selector(httpHeaders)])
{
[self setupStandardHeaders:response];

// Add optional response headers
if ([httpResponse respondsToSelector:@selector(httpHeaders)]) {
NSDictionary *responseHeaders = [httpResponse httpHeaders];

NSEnumerator *keyEnumerator = [responseHeaders keyEnumerator];
NSString *key;

while((key = [keyEnumerator nextObject]))
{
NSString *value = [responseHeaders objectForKey:key];

[response setHeaderField:key value:value];
}

NSArray *allKeys = [responseHeaders allKeys];
for (NSString *key in allKeys) {
NSString *value = [responseHeaders objectForKey:key];
[response setHeaderField:key value:value];
}
}

return [response messageData];
}

Expand Down Expand Up @@ -2458,7 +2472,7 @@ - (void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag
// finishBody method and forgot to call [super finishBody].
NSAssert(request == nil, @"Request not properly released in finishBody");

request = [[HTTPMessage alloc] initEmptyRequest];
request = [[HTTPMessage alloc] init];

numHeaderLines = 0;
sentResponseHeaders = NO;
Expand Down
6 changes: 0 additions & 6 deletions Core/HTTPMessage.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,8 @@


@interface HTTPMessage : NSObject
{
CFHTTPMessageRef message;
}

- (id)initEmptyRequest;

- (id)initRequestWithMethod:(NSString *)method URL:(NSURL *)url version:(NSString *)version;

- (id)initResponseWithStatusCode:(NSInteger)code description:(NSString *)description version:(NSString *)version;

- (BOOL)appendData:(NSData *)data;
Expand Down
Loading