Revert "NSURLSession Reimplementation (#411)"

This reverts commit 07233534e6.
This commit is contained in:
rfm 2024-07-02 19:19:14 +01:00 committed by GitHub
parent 07233534e6
commit 3fedf31c2d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
50 changed files with 7197 additions and 5391 deletions

View file

@ -1 +0,0 @@
Helpers/HTTPServer.bundle

View file

@ -1,8 +1,5 @@
include $(GNUSTEP_MAKEFILES)/common.make
ifeq ($(the_library_combo), ng-gnu-gnu)
SUBPROJECTS = Helpers
SUBPROJECTS = ../NSURLConnection/Helpers
include $(GNUSTEP_MAKEFILES)/aggregate.make
endif

View file

@ -1,20 +0,0 @@
include $(GNUSTEP_MAKEFILES)/common.make
BUNDLE_NAME = HTTPServer
NEEDS_GUI=NO
HTTPServer_OBJC_FILES = HTTPServer.m
HTTPServer_OBJC_LIBS += -ldispatch
ifeq ($(GNUSTEP_TARGET_OS), windows)
HTTPServer_OBJC_LIBS += -lws2_32
endif
HTTPServer_OBJCFLAGS += -fobjc-arc
#HTTPServer_RESOURCE_FILES += key.pem certificate.pem
HTTPServer_PRINCIPAL_CLASS = HTTPServer
-include GNUmakefile.preamble
include $(GNUSTEP_MAKEFILES)/bundle.make
-include GNUmakefile.postamble

View file

@ -1,30 +0,0 @@
#import <Foundation/NSArray.h>
#import <Foundation/NSObject.h>
#import <Foundation/NSURLRequest.h>
#import <Foundation/NSURLResponse.h>
typedef NSData * (^RequestHandlerBlock)(NSURLRequest *);
@interface Route : NSObject
+ (instancetype)routeWithURL:(NSURL *)url
method:(NSString *)method
handler:(RequestHandlerBlock)block;
- (NSString *)method;
- (NSURL *)url;
- (RequestHandlerBlock)block;
- (BOOL)acceptsURL:(NSURL *)url method:(NSString *)method;
@end
@interface HTTPServer : NSObject
- initWithPort:(NSInteger)port routes:(NSArray<Route *> *)routes;
- (NSInteger)port;
- (void)resume;
- (void)suspend;
- (void)setRoutes:(NSArray<Route *> *)routes;
@end

View file

@ -1,402 +0,0 @@
#import <Foundation/Foundation.h>
#import <dispatch/dispatch.h>
#ifdef _WIN32
#import <winsock2.h>
#define close(x) closesocket(x)
#else
#import <netinet/in.h>
#import <sys/socket.h>
#endif
#import "HTTPServer.h"
@interface
NSString (ServerAdditions)
- (void)enumerateLinesUsingBlock2:
(void (^)(NSString *line, NSUInteger lineEndIndex, BOOL *stop))block;
@end
@implementation
NSString (ServerAdditions)
- (void)enumerateLinesUsingBlock2:
(void (^)(NSString *line, NSUInteger lineEndIndex, BOOL *stop))block
{
NSUInteger length;
NSUInteger lineStart, lineEnd, contentsEnd;
NSRange currentLocationRange;
BOOL stop;
length = [self length];
lineStart = lineEnd = contentsEnd = 0;
stop = NO;
// Enumerate through the string line by line
while (lineStart < length && !stop)
{
NSString *line;
NSRange lineRange;
currentLocationRange = NSMakeRange(lineStart, 0);
[self getLineStart:&lineStart
end:&lineEnd
contentsEnd:&contentsEnd
forRange:currentLocationRange];
lineRange = NSMakeRange(lineStart, contentsEnd - lineStart);
line = [self substringWithRange:lineRange];
// Execute the block
block(line, lineEnd, &stop);
// Move to the next line
lineStart = lineEnd;
}
}
@end
/* We don't need this once toll-free bridging works */
NSData *
copyDispatchDataToNSData(dispatch_data_t dispatchData)
{
NSMutableData *mutableData =
[NSMutableData dataWithCapacity:dispatch_data_get_size(dispatchData)];
dispatch_data_apply(dispatchData, ^bool(dispatch_data_t region, size_t offset,
const void *buffer, size_t size) {
[mutableData appendBytes:buffer length:size];
return true; // Continue iterating
});
return [mutableData copy];
}
@implementation Route
{
NSString *_method;
NSURL *_url;
RequestHandlerBlock _block;
}
+ (instancetype)routeWithURL:(NSURL *)url
method:(NSString *)method
handler:(RequestHandlerBlock)block
{
return [[Route alloc] initWithURL:url method:method handler:block];
}
- (instancetype)initWithURL:(NSURL *)url
method:(NSString *)method
handler:(RequestHandlerBlock)block
{
self = [super init];
if (self)
{
_url = url;
_method = method;
_block = block;
}
return self;
}
- (NSString *)method
{
return _method;
}
- (NSURL *)url
{
return _url;
}
- (RequestHandlerBlock)block
{
return _block;
}
- (BOOL)acceptsURL:(NSURL *)url method:(NSString *)method
{
return [[_url path] isEqualTo:[url path]];
}
@end /* Route */
@implementation HTTPServer
{
_Atomic(BOOL) _stop;
int _socket;
NSInteger _port;
NSArray<Route *> *_routes;
dispatch_queue_t _queue;
dispatch_queue_t _acceptQueue;
}
- initWithPort:(NSInteger)port routes:(NSArray<Route *> *)routes
{
self = [super init];
if (!self)
{
return nil;
}
#ifdef _WIN32
WSADATA wsaData;
// Initialise WinSock2 API
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
{
NSLog(@"Error Creating Socket: %d", WSAGetLastError());
return nil;
}
#endif
_stop = YES;
_socket = socket(AF_INET, SOCK_STREAM, 0);
if (_socket == -1)
{
NSLog(@"Error creating socket %s", strerror(errno));
return nil;
}
_routes = [routes copy];
struct sockaddr_in serverAddr;
NSUInteger addrLen = sizeof(struct sockaddr_in);
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = NSSwapHostShortToBig(port);
serverAddr.sin_addr.s_addr = INADDR_ANY;
int rc;
int yes = 1;
rc = setsockopt(_socket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
if (rc == -1)
{
NSLog(@"Error setting socket options %s", strerror(errno));
return nil;
}
rc = bind(_socket, (struct sockaddr *) &serverAddr, sizeof(struct sockaddr));
if (rc < 0)
{
NSLog(@"Error binding to socket %s", strerror(errno));
return nil;
}
// Get Port Number
if (getsockname(_socket, (struct sockaddr *) &serverAddr, &addrLen) == -1)
{
NSLog(@"Error getting socket name %s", strerror(errno));
return nil;
}
_port = NSSwapBigShortToHost(serverAddr.sin_port);
rc = listen(_socket, 20);
if (rc < 0)
{
NSLog(@"Error listening on socket %s", strerror(errno));
return nil;
}
_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
_acceptQueue = dispatch_queue_create("org.gnustep.HTTPServer.AcceptQueue",
DISPATCH_QUEUE_CONCURRENT);
return self;
}
- (void)acceptConnection
{
struct sockaddr_in clientAddr;
dispatch_io_t ioChannel;
NSUInteger sin_size;
int clientSocket;
sin_size = sizeof(struct sockaddr_in);
clientSocket = accept(_socket, (struct sockaddr *) &clientAddr, &sin_size);
if (clientSocket < 0)
{
NSLog(@"Error accepting connection %s", strerror(errno));
return;
}
ioChannel
= dispatch_io_create(DISPATCH_IO_STREAM, clientSocket, _queue,
^(int error) {
close(clientSocket);
if (error)
{
NSLog(@"Error creating dispatch I/O channel %s",
strerror(error));
return;
}
});
dispatch_io_set_low_water(ioChannel, 1);
dispatch_io_read(ioChannel, 0, SIZE_MAX, _queue,
^(bool done, dispatch_data_t data, int error) {
if (error)
{
NSLog(@"Error reading data %s", strerror(error));
dispatch_io_close(ioChannel, DISPATCH_IO_STOP);
return;
}
if (data && dispatch_data_get_size(data) != 0)
{
[self handleConnectionData:data
forSocket:clientSocket];
}
if (done)
{
dispatch_io_close(ioChannel, DISPATCH_IO_STOP);
}
});
}
- (void)handleConnectionData:(dispatch_data_t)data forSocket:(int)sock
{
NSData *reqData;
NSString *reqString;
NSRange bodyRange;
NSString *method, *url, *version;
NSURL *requestURL;
NSScanner *scanner;
Route *selectedRoute = nil;
__block NSString *firstLine = nil;
__block NSMutableURLRequest *request = [NSMutableURLRequest new];
__block NSUInteger headerEndIndex = 1;
reqData = copyDispatchDataToNSData(data);
reqString = [[NSString alloc] initWithData:reqData
encoding:NSUTF8StringEncoding];
/*
* generic-message = Request-Line
* *(message-header CRLF)
* CRLF
* [ message-body ]
* Request-Line = Method SP Request-URI SP HTTP-Version CRLF
*/
[reqString enumerateLinesUsingBlock2:^(NSString *line,
NSUInteger lineEndIndex, BOOL *stop) {
NSRange range;
NSString *key, *value;
NSCharacterSet *set;
set = [NSCharacterSet whitespaceCharacterSet];
/* Parse Request Line */
if (nil == firstLine)
{
firstLine = [line stringByTrimmingCharactersInSet:set];
return;
}
/* Reached end of message header. Stop. */
if ([line length] == 0)
{
*stop = YES;
headerEndIndex = lineEndIndex;
}
range = [line rangeOfString:@":"];
/* Ignore this line */
if (NSNotFound == range.location)
{
return;
}
key = [[line substringToIndex:range.location]
stringByTrimmingCharactersInSet:set];
value = [[line substringFromIndex:range.location + 1]
stringByTrimmingCharactersInSet:set];
[request addValue:value forHTTPHeaderField:key];
}];
/* Calculate remaining body range */
bodyRange = NSMakeRange(headerEndIndex, [reqData length] - headerEndIndex);
reqData = [reqData subdataWithRange:bodyRange];
/* Parse Request Line */
scanner = [NSScanner scannerWithString:firstLine];
[scanner scanUpToString:@" " intoString:&method];
[scanner scanUpToString:@" " intoString:&url];
[scanner scanUpToString:@" " intoString:&version];
requestURL = [NSURL URLWithString:url];
[request setURL:requestURL];
[request setHTTPMethod:method];
[request setHTTPBody:reqData];
for (Route *r in _routes)
{
if ([r acceptsURL:requestURL method:method])
{
selectedRoute = r;
break;
}
}
NSData *responseData;
if (selectedRoute)
{
responseData = [selectedRoute block]([request copy]);
}
else
{
responseData = [@"HTTP/1.1 404 Not Found\r\nContent-Length: 0\r\n\r\n"
dataUsingEncoding:NSASCIIStringEncoding];
}
send(sock, [responseData bytes], [responseData length], 0);
}
- (void)setRoutes:(NSArray<Route *> *)routes
{
_routes = [routes copy];
}
- (NSInteger)port
{
return _port;
}
- (void)resume
{
if (_stop)
{
_stop = NO;
dispatch_async(_acceptQueue, ^{
while (!_stop)
{
[self acceptConnection];
}
});
}
}
- (void)suspend
{
_stop = YES;
}
- (void)dealloc
{
#ifndef __APPLE__
dispatch_release(_acceptQueue);
#endif
close(_socket);
#ifdef _WIN32
WSACleanup();
#endif
}
@end

View file

@ -1,26 +0,0 @@
#import <Foundation/NSDate.h>
#import <Foundation/NSRunLoop.h>
@interface
NSRunLoop (TimeOutAdditions)
- (void)runForSeconds:(NSTimeInterval)seconds conditionBlock:(BOOL (^)())block;
@end
@implementation
NSRunLoop (TimeOutAdditions)
- (void)runForSeconds:(NSTimeInterval)seconds conditionBlock:(BOOL (^)())block
{
NSDate *startDate = [NSDate date];
NSTimeInterval endTime = [startDate timeIntervalSince1970] + seconds;
NSTimeInterval interval = 0.1; // Interval to check the condition
while (block() && [[NSDate date] timeIntervalSince1970] < endTime)
{
@autoreleasepool
{
[[NSRunLoop currentRunLoop]
runUntilDate:[NSDate dateWithTimeIntervalSinceNow:interval]];
}
}
}
@end

View file

@ -1,54 +0,0 @@
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas volutpat justo
in aliquet venenatis. Aliquam eu odio volutpat, euismod turpis a, accumsan leo.
Aliquam mauris urna, dictum a ex vel, fermentum faucibus enim. Donec eu massa
ipsum. Vestibulum diam metus, dictum mattis urna vel, dignissim pretium magna.
Phasellus quis mattis felis. Morbi non dapibus nunc. Fusce id ligula fringilla,
varius tortor et, vulputate arcu. Quisque justo dolor, bibendum et imperdiet
ultricies, posuere vel elit. Aliquam vel risus et sapien malesuada tempor.
Aenean lorem metus, rutrum ut massa non, ultrices tincidunt neque. Nullam
viverra rhoncus sem et convallis.
Sed id ligula non mauris lacinia mattis. Donec commodo tortor sed aliquet
commodo. Curabitur auctor, nibh eu dignissim gravida, lacus eros tincidunt
metus, ac fermentum mauris ex vitae magna. Vestibulum sed egestas elit, eu
placerat enim. Vivamus ullamcorper sollicitudin ullamcorper. Mauris consequat
quam et purus malesuada, quis euismod nisl scelerisque. Sed vestibulum dictum
nisi a viverra. Ut rutrum aliquam porta. Pellentesque est augue, ullamcorper vel
ultricies quis, posuere non turpis. Aenean suscipit nulla massa, vel venenatis
felis molestie ut. Morbi in lorem quam. Quisque sed facilisis neque, et mattis
ligula. Nam non placerat odio, ac sollicitudin dolor. Donec elit neque, rhoncus
at risus fermentum, ultrices hendrerit massa. Nulla volutpat, urna id molestie
scelerisque, ligula est laoreet mauris, eget maximus leo purus id mi.
Aenean arcu metus, ornare eget est sit amet, vehicula accumsan enim. Morbi ante
diam, faucibus nec tempus molestie, vulputate auctor massa. Curabitur posuere
iaculis pulvinar. Fusce id ligula at justo iaculis accumsan eu sed velit.
Quisque condimentum augue non lobortis finibus. In quis interdum ligula, a
varius ex. Vestibulum tristique orci eget velit lacinia, at lacinia lorem
pretium. Sed ut varius mi. Vivamus elementum luctus arcu, vitae porta felis
cursus nec.
Etiam sed nisl eu nulla scelerisque fermentum. Praesent blandit eros at lacus
blandit auctor. Sed in eros sapien. Quisque ac laoreet nisi, a fringilla nunc.
Sed auctor eros non mauris elementum vestibulum. Aenean vel urna orci.
Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia
curae; In sed blandit nibh. Aenean condimentum, lorem a tincidunt consequat,
nulla nulla ornare lorem, eu cursus sapien mi aliquam arcu. Duis porttitor mi et
ultricies porta. Donec tempus posuere lorem sit amet tincidunt. Nam nec purus
sit amet sem accumsan consectetur. Ut a nisl volutpat, egestas diam non, feugiat
mi.
Cras sed risus eget ligula tincidunt luctus. Vestibulum ut sapien dictum,
sagittis elit vel, semper diam. Curabitur tristique ullamcorper lacus, quis
finibus ex rutrum vel. Phasellus non blandit nisl, id tincidunt augue. Integer
commodo metus at augue fringilla, sit amet posuere lectus iaculis. In nunc
lacus, pulvinar scelerisque faucibus ut, fermentum ac ante. Duis in ante
faucibus, ullamcorper est vel, convallis lacus. Nulla fermentum, nisi eget
sollicitudin sodales, augue felis gravida est, id luctus sem augue non turpis.
Sed a lorem vel diam maximus malesuada ut ut leo. Fusce venenatis, tellus a
scelerisque dictum, metus elit vehicula purus, id gravida libero libero vitae
arcu. Curabitur a cursus metus. Aenean efficitur rhoncus ante vitae imperdiet.
Etiam ante lacus, aliquam id sem non, ullamcorper interdum libero. Sed fringilla
dui sed est vehicula, vitae sollicitudin mauris tristique. Praesent ut arcu ut
neque sollicitudin finibus. Vestibulum lacus risus, egestas vel lorem eget,
pellentesque scelerisque lectus.

View file

@ -1,224 +0,0 @@
#import <Foundation/NSURLSession.h>
#import <Foundation/NSData.h>
@class URLManager;
typedef void (^URLManagerCheckBlock)(URLManager *);
@interface URLManager
: NSObject <NSURLSessionDataDelegate, NSURLSessionTaskDelegate,
NSURLSessionDelegate, NSURLSessionDownloadDelegate>
{
URLManagerCheckBlock _checkBlock;
@public
NSURLSession *currentSession;
NSURLSessionResponseDisposition responseAnswer;
NSInteger numberOfExpectedTasksBeforeCheck;
NSInteger didCreateTaskCount;
NSURLSessionTask *didCreateTask;
NSInteger didBecomeInvalidCount;
NSError *didBecomeInvalidError;
NSInteger httpRedirectionCount;
NSURLSessionTask *httpRedirectionTask;
NSHTTPURLResponse *httpRedirectionResponse;
NSURLRequest *httpRedirectionRequest;
NSInteger didCompleteCount;
NSURLSessionTask *didCompleteTask;
NSError *didCompleteError;
NSInteger didWriteDataCount;
NSURLSessionDownloadTask *didWriteDataTask;
int64_t downloadBytesWritten;
int64_t downloadTotalBytesWritten;
int64_t downloadTotalBytesExpectedToWrite;
NSInteger didFinishDownloadingCount;
NSURLSessionDownloadTask *didFinishDownloadingTask;
NSURL *didFinishDownloadingURL;
NSInteger didReceiveResponseCount;
NSURLSessionDataTask *didReceiveResponseTask;
NSURLResponse *didReceiveResponse;
NSInteger didReceiveDataCount;
NSURLSessionDataTask *didReceiveDataTask;
NSMutableData *accumulatedData;
BOOL cancelRedirect;
}
- (void)setCheckBlock:(URLManagerCheckBlock)block;
@end
@implementation URLManager
- (instancetype)init
{
self = [super init];
if (self)
{
responseAnswer = NSURLSessionResponseAllow;
accumulatedData = [[NSMutableData alloc] init];
}
return self;
}
- (void)setCheckBlock:(URLManagerCheckBlock)block
{
_checkBlock = _Block_copy(block);
}
#pragma mark - Session Lifecycle
- (void)URLSession:(NSURLSession *)session
didCreateTask:(NSURLSessionTask *)task
{
ASSIGN(currentSession, session);
didCreateTaskCount += 1;
ASSIGN(didCreateTask, task);
}
- (void)URLSession:(NSURLSession *)session
didBecomeInvalidWithError:(NSError *)error
{
ASSIGN(currentSession, session);
ASSIGN(didBecomeInvalidError, error);
didBecomeInvalidCount += 1;
}
#pragma mark - Task Updates
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
willPerformHTTPRedirection:(NSHTTPURLResponse *)response
newRequest:(NSURLRequest *)request
completionHandler:(void (^)(NSURLRequest *))completionHandler
{
ASSIGN(currentSession, session);
ASSIGN(httpRedirectionTask, task);
ASSIGN(httpRedirectionResponse, response);
ASSIGN(httpRedirectionRequest, request);
if (cancelRedirect)
{
completionHandler(NULL);
}
else
{
completionHandler(request);
}
httpRedirectionCount += 1;
}
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
didCompleteWithError:(nullable NSError *)error
{
ASSIGN(currentSession, session);
ASSIGN(didCompleteTask, task);
ASSIGN(didCompleteError, error);
didCompleteCount += 1;
if (didCompleteCount == numberOfExpectedTasksBeforeCheck
&& _checkBlock != NULL)
{
_checkBlock(self);
}
}
#pragma mark - Data Updates
- (void)URLSession:(NSURLSession *)session
dataTask:(NSURLSessionDataTask *)dataTask
didReceiveResponse:(NSURLResponse *)response
completionHandler:
(void (^)(NSURLSessionResponseDisposition))completionHandler
{
ASSIGN(currentSession, session);
ASSIGN(didReceiveResponseTask, dataTask);
ASSIGN(didReceiveResponse, response);
didReceiveResponseCount += 1;
completionHandler(responseAnswer);
}
- (void)URLSession:(NSURLSession *)session
dataTask:(NSURLSessionDataTask *)dataTask
didReceiveData:(NSData *)data
{
ASSIGN(currentSession, session);
ASSIGN(didReceiveResponseTask, dataTask);
didReceiveDataCount += 1;
[accumulatedData appendData:data];
}
#pragma mark - Download Updates
- (void)URLSession:(NSURLSession *)session
downloadTask:(NSURLSessionDownloadTask *)downloadTask
didWriteData:(int64_t)bytesWritten
totalBytesWritten:(int64_t)totalBytesWritten
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
ASSIGN(currentSession, session);
didWriteDataCount += 1;
downloadBytesWritten = bytesWritten;
downloadTotalBytesExpectedToWrite = totalBytesExpectedToWrite;
downloadTotalBytesExpectedToWrite = totalBytesExpectedToWrite;
}
- (void)URLSession:(NSURLSession *)session
downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location
{
ASSIGN(currentSession, session);
ASSIGN(didFinishDownloadingTask, downloadTask);
ASSIGN(didFinishDownloadingURL, location);
didFinishDownloadingCount += 1;
}
- (void)dealloc
{
RELEASE(currentSession);
RELEASE(didCreateTask);
RELEASE(didBecomeInvalidError);
RELEASE(httpRedirectionTask);
RELEASE(httpRedirectionResponse);
RELEASE(httpRedirectionRequest);
RELEASE(didCompleteTask);
RELEASE(didCompleteError);
RELEASE(didWriteDataTask);
RELEASE(didFinishDownloadingTask);
RELEASE(didFinishDownloadingURL);
RELEASE(didReceiveResponseTask);
RELEASE(didReceiveResponse);
RELEASE(didReceiveDataTask);
RELEASE(accumulatedData);
_Block_release(_checkBlock);
[super dealloc];
}
@end

View file

@ -0,0 +1,131 @@
#import <Foundation/Foundation.h>
#import "Testing.h"
#import "ObjectTesting.h"
@interface MyDelegate : NSObject <NSURLSessionDelegate>
{
BOOL _finished;
NSMutableArray *_order;
NSURLResponse *_response;
NSData *_taskData;
NSString *_taskText;
NSError *_taskError;
NSURL *_taskLocation;
}
- (BOOL) finished;
- (void) reset;
@end
@implementation MyDelegate
- (void) dealloc
{
[self reset];
[super dealloc];
}
- (BOOL) finished
{
return _finished;
}
- (id) init
{
if (nil != (self = [super init]))
{
_order = [NSMutableArray new];
}
return self;
}
- (NSMutableArray*) order
{
return _order;
}
- (void) reset
{
DESTROY(_order);
DESTROY(_response);
DESTROY(_taskData);
DESTROY(_taskError);
DESTROY(_taskText);
DESTROY(_taskLocation);
_finished = NO;
}
- (NSURLResponse*) response
{
return _response;
}
- (NSData*) taskData
{
return _taskData;
}
- (NSError*) taskError
{
return _taskError;
}
- (NSString*) taskText
{
return _taskText;
}
- (NSURL*) taskLocation
{
return _taskLocation;
}
- (void) URLSession: (NSURLSession*)session
dataTask: (NSURLSessionDataTask*)dataTask
didReceiveResponse: (NSURLResponse*)response
completionHandler: (void (^)(NSURLSessionResponseDisposition disposition))completionHandler
{
[_order addObject: NSStringFromSelector(_cmd)];
ASSIGN(_response, response);
completionHandler(NSURLSessionResponseAllow);
}
- (void) URLSession: (NSURLSession*)session
dataTask: (NSURLSessionDataTask*)dataTask
didReceiveData: (NSData*)data
{
[_order addObject: NSStringFromSelector(_cmd)];
NSString *text;
ASSIGN(_taskData, data);
text = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];
if (nil != text)
{
ASSIGN(_taskText, text);
}
RELEASE(text);
}
/* NSURLSessionDownloadDelegate */
- (void) URLSession: (NSURLSession *)session
downloadTask: (NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL: (NSURL *)location
{
ASSIGN(_taskLocation, location);
}
- (void) URLSession: (NSURLSession*)session
task: (NSURLSessionTask*)task
didCompleteWithError: (NSError*)error
{
[_order addObject: NSStringFromSelector(_cmd)];
_finished = YES;
if (error == nil)
{
NSLog(@"Download is Successful");
}
else
{
NSLog(@"Error %@", [error userInfo]);
}
ASSIGN(_taskError, error);
}
@end

View file

@ -0,0 +1,79 @@
def print_ivar
set $adr = malloc(1000)
call strcpy(($adr),($arg1))
call GSObjCPrint(($arg0),($adr))
call free($adr)
end
def print_ivars
if ((($arg0)->isa->info & 0x01) || (($arg0)->isa->info == 2816))
# arg0 is a pointer to an object
set $cls = ($arg0)->isa
else
if (($arg0)->isa->info & 0x02)
# arg0 is a pointer to a class
set $cls = ($arg0)
else
# arg0 is something else
set $cls = 0
end
end
while (($cls) != 0)
set $ivars = ($cls)->ivars
if (($ivars) != 0)
set $i = 0
while ($i < ($ivars)->count)
output ($ivars)->ivar_list[$i]
echo \n
set $i = $i + 1
end
end
set $cls = ($cls)->super_class
end
end
def pivi
print *(int *)((char *)($arg0) + ($arg1)))
end
def pivl
print *(long *)((char *)($arg0) + ($arg1)))
end
def pivp
print *(void *)((char *)($arg0) + ($arg1)))
end
def pivo
po *((id *)((char *)($arg0) + ($arg1)))
end
document print_ivars
Recursively print the instance varibles of the object or a class given
as first (and only) argument.
end
document pivi
Print the value of the an instance variable as an int.
The first argument is the pointer to the object and the second the
offset of the instance variable.
end
document pivl
Print the value of the an instance variable as a long.
The first argument is the pointer to the object and the second the
offset of the instance variable.
end
document pivp
Print the value of the an instance variable as a pointer (to void).
The first argument is the pointer to the object and the second the
offset of the instance variable.
end
document pivo
Ask an instance variable to print itself (using po).
The first argument is the pointer to the object and the second the
offset of the instance variable.
end

View file

@ -1,886 +0,0 @@
#import <Foundation/Foundation.h>
#if GS_HAVE_NSURLSESSION
#import "Helpers/HTTPServer.h"
#import "NSRunLoop+TimeOutAdditions.h"
#import "URLManager.h"
#import "Testing.h"
/* Timeout in Seconds */
static NSInteger testTimeOut = 60;
static NSTimeInterval expectedCountOfTasksToComplete = 0;
/* Accessed in delegate on different thread.
*/
static _Atomic(NSInteger) currentCountOfCompletedTasks = 0;
static NSLock *countLock;
static NSDictionary *requestCookieProperties;
static NSArray<Route *> *
createRoutes(Class routeClass, NSURL *baseURL)
{
Route *routeOKWithContent;
Route *routeTmpRedirectToOK;
Route *routeTmpRelativeRedirectToOK;
Route *routeSetCookiesOK;
Route *routeFoldedHeaders;
Route *routeIncorrectlyFoldedHeaders;
NSURL *routeOKWithContentURL;
NSURL *routeTmpRedirectToOKURL;
NSURL *routeTmpRelativeRedirectToOKURL;
NSURL *routeSetCookiesOKURL;
NSURL *routeFoldedHeadersURL;
NSURL *routeIncorrectlyFoldedHeadersURL;
routeOKWithContentURL = [NSURL URLWithString:@"/contentOK"];
routeTmpRedirectToOKURL = [NSURL URLWithString:@"/tmpRedirectToOK"];
routeTmpRelativeRedirectToOKURL =
[NSURL URLWithString:@"/tmpRelativeRedirectToOK"];
routeSetCookiesOKURL = [NSURL URLWithString:@"/setCookiesOK"];
routeFoldedHeadersURL = [NSURL URLWithString:@"/foldedHeaders"];
routeIncorrectlyFoldedHeadersURL =
[NSURL URLWithString:@"/incorrectFoldedHeaders"];
routeOKWithContent = [routeClass
routeWithURL:routeOKWithContentURL
method:@"GET"
handler:^NSData *(NSURLRequest *req) {
NSData *response;
response =
[@"HTTP/1.1 200 OK\r\nContent-Length: 12\r\n\r\nHello World!"
dataUsingEncoding:NSASCIIStringEncoding];
return response;
}];
routeTmpRedirectToOK = [routeClass
routeWithURL:routeTmpRedirectToOKURL
method:@"GET"
handler:^NSData *(NSURLRequest *req) {
NSData *response;
NSString *responseString;
responseString = [NSString
stringWithFormat:@"HTTP/1.1 307 Temporary Redirect\r\nLocation: "
@"%@\r\nContent-Length: 0\r\n\r\n",
[baseURL
URLByAppendingPathComponent:@"contentOK"]];
response = [responseString dataUsingEncoding:NSASCIIStringEncoding];
return response;
}];
routeTmpRelativeRedirectToOK = [routeClass
routeWithURL:routeTmpRelativeRedirectToOKURL
method:@"GET"
handler:^NSData *(NSURLRequest *req) {
NSData *response;
NSString *responseString;
responseString = [NSString
stringWithFormat:@"HTTP/1.1 307 Temporary Redirect\r\nLocation: "
@"contentOK\r\nContent-Length: 0\r\n\r\n"];
response = [responseString dataUsingEncoding:NSASCIIStringEncoding];
return response;
}];
routeSetCookiesOK = [routeClass
routeWithURL:routeSetCookiesOKURL
method:@"GET"
handler:^NSData *(NSURLRequest *req) {
NSData *response;
NSString *httpResponse;
httpResponse = @"HTTP/1.1 200 OK\r\n"
"Content-Type: text/html; charset=UTF-8\r\n"
"Set-Cookie: sessionId=abc123; Expires=Wed, 09 Jun "
"2100 10:18:14 GMT; Path=/\r\n"
"Content-Length: 13\r\n"
"\r\n"
"Hello, world!";
NSString *cookie = [req allHTTPHeaderFields][@"Cookie"];
PASS(cookie != nil, "Cookie field is not nil");
PASS([cookie containsString:@"RequestCookie=1234"],
"cookie contains request cookie");
response = [httpResponse dataUsingEncoding:NSASCIIStringEncoding];
return response;
}];
routeFoldedHeaders = [routeClass
routeWithURL:routeFoldedHeadersURL
method:@"GET"
handler:^NSData *(NSURLRequest *req) {
NSData *response;
response =
[@"HTTP/1.1 200 OK\r\nContent-Length: 12\r\nFolded-Header-SP: "
@"Test\r\n ing\r\nFolded-Header-TAB: Test\r\n\ting\r\n\r\nHello "
@"World!" dataUsingEncoding:NSASCIIStringEncoding];
return response;
}];
routeIncorrectlyFoldedHeaders = [routeClass
routeWithURL:routeIncorrectlyFoldedHeadersURL
method:@"GET"
handler:^NSData *(NSURLRequest *req) {
NSData *response;
response = [@"HTTP/1.1 200 OK\r\n"
@" ing\r\nFolded-Header-TAB: Test\r\n\ting\r\n\r\nHello "
@"World!" dataUsingEncoding:NSASCIIStringEncoding];
return response;
}];
return @[
routeOKWithContent, routeTmpRedirectToOK, routeTmpRelativeRedirectToOK,
routeSetCookiesOK, routeFoldedHeaders, routeIncorrectlyFoldedHeaders
];
}
/* Block needs to be released */
static URLManagerCheckBlock
downloadCheckBlock(const char *prefix, NSURLSession *session,
NSURLSessionTask *task)
{
return _Block_copy(^(URLManager *mgr) {
NSURL *location;
NSData *data;
NSString *string;
NSFileManager *fm;
location = mgr->didFinishDownloadingURL;
fm = [NSFileManager defaultManager];
PASS_EQUAL(mgr->currentSession, session,
"%s URLManager Session is equal to session", prefix);
/* Check URLSession:didCreateTask: callback */
PASS(mgr->didCreateTaskCount == 1, "%s didCreateTask: Count is correct",
prefix);
PASS_EQUAL(mgr->didCreateTask, task,
"%s didCreateTask: task is equal to returned task", prefix);
/* Check URLSession:task:didCompleteWithError: */
PASS(nil == mgr->didCompleteError,
"%s didCompleteWithError: No error occurred", prefix)
PASS(mgr->didCompleteCount == 1,
"%s didCompleteWithError: Count is correct", prefix);
PASS_EQUAL(mgr->didCompleteTask, task,
"%s didCompleteWithError: task is equal to returned task",
prefix);
/* Check Progress Reporting */
PASS(mgr->didWriteDataCount == 1, "%s didWriteData: count is correct",
prefix);
PASS(mgr->downloadTotalBytesWritten
== mgr->downloadTotalBytesExpectedToWrite,
"%s didWriteData: Downloaded all expected data", prefix);
PASS(nil != mgr->didFinishDownloadingURL,
"%s didWriteData: Download location is not nil", prefix);
PASS([location isFileURL], "%s location is a fileURL", prefix);
data = [NSData dataWithContentsOfURL:location];
PASS(nil != data, "%s dataWithContentsOfURL is not nil", prefix)
string = [[NSString alloc] initWithData:data
encoding:NSASCIIStringEncoding];
PASS(nil != string, "%s string from data is not nil", prefix);
PASS_EQUAL(string, @"Hello World!", "%s data is correct", prefix);
[string release];
/* Remove Downloaded Item */
if (location)
{
[fm removeItemAtURL:location error:NULL];
}
[countLock lock];
currentCountOfCompletedTasks += 1;
[countLock unlock];
});
}
static URLManagerCheckBlock
dataCheckBlock(const char *prefix, NSURLSession *session,
NSURLSessionTask *task)
{
return _Block_copy(^(URLManager *mgr) {
PASS_EQUAL(mgr->currentSession, session,
"%s URLManager Session is equal to session", prefix);
/* Check URLSession:didCreateTask: callback */
PASS(mgr->didCreateTaskCount == 1, "%s didCreateTask: Count is correct",
prefix);
PASS_EQUAL(mgr->didCreateTask, task,
"%s didCreateTask: task is equal to returned task", prefix);
/* Check URLSession:task:didCompleteWithError: */
PASS(nil == mgr->didCompleteError,
"%s didCompleteWithError: No error occurred", prefix)
PASS(mgr->didCompleteCount == 1,
"%s didCompleteWithError: Count is correct", prefix);
PASS_EQUAL(mgr->didCompleteTask, task,
"%s didCompleteWithError: task is equal to returned task",
prefix);
NSData *data = mgr->accumulatedData;
PASS(mgr->didReceiveDataCount == 1, "%s didReceiveData: Count is correct",
prefix);
PASS(nil != data, "%s data in didReceiveData is not nil", prefix);
NSString *string = [[NSString alloc] initWithData:data
encoding:NSASCIIStringEncoding];
PASS(nil != string, "%s string from data is not nil", prefix);
PASS_EQUAL(string, @"Hello World!", "%s data is correct", prefix);
[string release];
[countLock lock];
currentCountOfCompletedTasks += 1;
[countLock unlock];
});
}
/* Block needs to be released */
static URLManagerCheckBlock
dataCheckBlockFailedRequest(const char *prefix, NSURLSession *session,
NSURLSessionTask *task)
{
return _Block_copy(^(URLManager *mgr) {
PASS_EQUAL(mgr->currentSession, session,
"%s URLManager Session is equal to session", prefix);
/* Check URLSession:didCreateTask: callback */
PASS(mgr->didCreateTaskCount == 1, "%s didCreateTask: Count is correct",
prefix);
PASS_EQUAL(mgr->didCreateTask, task,
"%s didCreateTask: task is equal to returned task", prefix);
/* Check URLSession:task:didCompleteWithError: */
PASS(nil != mgr->didCompleteError,
"%s didCompleteWithError: An error occurred", prefix)
PASS(mgr->didCompleteCount == 1,
"%s didCompleteWithError: Count is correct", prefix);
PASS_EQUAL(mgr->didCompleteTask, task,
"%s didCompleteWithError: task is equal to returned task",
prefix);
/* Check didReceiveResponse if not a canceled redirect */
if (!mgr->cancelRedirect)
{
PASS(mgr->didReceiveResponseCount == 1,
"%s didReceiveResponse: Count is correct", prefix);
PASS(nil != mgr->didReceiveResponse, "%s didReceiveResponse is not nil",
prefix);
PASS_EQUAL(mgr->didReceiveResponseTask, task,
"%s didReceiveResponse: task is equal to returned task",
prefix);
}
else
{
PASS_EQUAL([mgr->didCompleteError code], NSURLErrorCancelled,
"%s didCompleteError is NSURLErrorCancelled", prefix);
}
[countLock lock];
currentCountOfCompletedTasks += 1;
[countLock unlock];
});
}
/* Creates a downloadTaskWithURL: with the /contentOK route.
*
* Delegate callbacks are checked via the URLManager checkBlock.
*/
static URLManager *
testSimpleDownloadTransfer(NSURL *baseURL)
{
NSURLSession *session;
NSURLSessionConfiguration *configuration;
NSURLSessionDownloadTask *task;
URLManager *mgr;
URLManagerCheckBlock block;
const char *prefix = "<DownloadTransfer>";
NSURL *contentOKURL;
/* URL Delegate Setup */
mgr = [URLManager new];
mgr->numberOfExpectedTasksBeforeCheck = 1;
expectedCountOfTasksToComplete += 1;
/* URL Setup */
contentOKURL = [baseURL URLByAppendingPathComponent:@"contentOK"];
configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
session = [NSURLSession sessionWithConfiguration:configuration
delegate:mgr
delegateQueue:nil];
task = [session downloadTaskWithURL:contentOKURL];
PASS(nil != task, "%s Session created a valid download task", prefix);
/* Setup Check Block */
block = downloadCheckBlock(prefix, session, task);
[mgr setCheckBlock:block];
_Block_release(block);
[task resume];
return mgr;
}
static URLManager *
testDownloadTransferWithRedirect(NSURL *baseURL)
{
NSURLSession *session;
NSURLSessionConfiguration *configuration;
NSURLSessionDownloadTask *task;
URLManager *mgr;
URLManagerCheckBlock block;
const char *prefix = "<DownloadTransferWithRedirect>";
NSURL *contentOKURL;
/* URL Delegate Setup */
mgr = [URLManager new];
mgr->numberOfExpectedTasksBeforeCheck = 1;
expectedCountOfTasksToComplete += 1;
/* URL Setup */
contentOKURL = [baseURL URLByAppendingPathComponent:@"tmpRedirectToOK"];
configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
session = [NSURLSession sessionWithConfiguration:configuration
delegate:mgr
delegateQueue:nil];
task = [session downloadTaskWithURL:contentOKURL];
PASS(nil != task, "%s Session created a valid download task", prefix);
/* Setup Check Block */
block = downloadCheckBlock(prefix, session, task);
[mgr setCheckBlock:block];
_Block_release(block);
[task resume];
return mgr;
}
/* This should use the build in redirection system from libcurl */
static void
testDataTransferWithRedirectAndBlock(NSURL *baseURL)
{
NSURLSession *session;
NSURLSessionDataTask *task;
NSURL *url;
expectedCountOfTasksToComplete += 1;
session = [NSURLSession sharedSession];
url = [baseURL URLByAppendingPathComponent:@"tmpRedirectToOK"];
task = [session
dataTaskWithURL:url
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSString *string;
const char *prefix = "<DataTransferWithRedirectAndBlock>";
PASS(nil != data, "%s data in completion handler is not nil", prefix);
PASS(nil != response, "%s response is not nil", prefix);
PASS([response isKindOfClass:[NSHTTPURLResponse class]],
"%s response is an NSHTTPURLResponse", prefix);
PASS(nil == error, "%s error is nil", prefix);
string = [[NSString alloc] initWithData:data
encoding:NSASCIIStringEncoding];
PASS_EQUAL(string, @"Hello World!", "%s received data is correct",
prefix);
[string release];
[countLock lock];
currentCountOfCompletedTasks += 1;
[countLock unlock];
}];
[task resume];
}
static void
testDataTransferWithCanceledRedirect(NSURL *baseURL)
{
NSURLSession *session;
NSURLSessionConfiguration *configuration;
NSURLSessionDataTask *task;
URLManager *mgr;
URLManagerCheckBlock block;
const char *prefix = "<DataTransferWithCanceledRedirect>";
NSURL *contentOKURL;
/* URL Delegate Setup */
mgr = [URLManager new];
mgr->numberOfExpectedTasksBeforeCheck = 1;
mgr->cancelRedirect = YES;
expectedCountOfTasksToComplete += 1;
/* URL Setup */
contentOKURL = [baseURL URLByAppendingPathComponent:@"tmpRedirectToOK"];
configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
session = [NSURLSession sessionWithConfiguration:configuration
delegate:mgr
delegateQueue:nil];
task = [session dataTaskWithURL:contentOKURL];
PASS(nil != task, "%s Session created a valid download task", prefix);
/* Setup Check Block */
block = dataCheckBlockFailedRequest(prefix, session, task);
[mgr setCheckBlock:block];
_Block_release(block);
[task resume];
}
static void
testDataTransferWithRelativeRedirect(NSURL *baseURL)
{
NSURLSession *session;
NSURLSessionConfiguration *configuration;
NSURLSessionDataTask *task;
NSURL *url;
URLManager *mgr;
URLManagerCheckBlock block;
const char *prefix = "<DataTransferWithRelativeRedirect>";
/* URL Delegate Setup */
mgr = [URLManager new];
mgr->numberOfExpectedTasksBeforeCheck = 1;
expectedCountOfTasksToComplete += 1;
session = [NSURLSession sharedSession];
url = [baseURL URLByAppendingPathComponent:@"tmpRelativeRedirectToOK"];
configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
session = [NSURLSession sessionWithConfiguration:configuration
delegate:mgr
delegateQueue:nil];
task = [session dataTaskWithURL:url];
PASS(nil != task, "%s Session created a valid download task", prefix);
/* Setup Check Block */
block = dataCheckBlock(prefix, session, task);
[mgr setCheckBlock:block];
_Block_release(block);
[task resume];
}
static void
testDownloadTransferWithBlock(NSURL *baseURL)
{
NSURLSession *session;
NSURLSessionDownloadTask *task;
NSURL *url;
expectedCountOfTasksToComplete += 1;
session = [NSURLSession sharedSession];
url = [baseURL URLByAppendingPathComponent:@"contentOK"];
task = [session
downloadTaskWithURL:url
completionHandler:^(NSURL *location, NSURLResponse *response,
NSError *error) {
NSFileManager *fm;
NSData *data;
NSString *string;
const char *prefix;
prefix = "<DownloadTransferWithBlock>";
fm = [NSFileManager defaultManager];
PASS(nil != location, "%s location is not nil", prefix);
PASS(nil != response, "%s response is not nil", prefix);
PASS(nil == error, "%s error is nil", prefix);
data = [NSData dataWithContentsOfURL:location];
PASS(nil != data, "%s data is not nil", prefix);
string = [[NSString alloc] initWithData:data
encoding:NSASCIIStringEncoding];
PASS_EQUAL(string, @"Hello World!", "%s content is correct", prefix);
[fm removeItemAtURL:location error:NULL];
[countLock lock];
currentCountOfCompletedTasks += 1;
[countLock unlock];
[string release];
}];
[task resume];
}
static URLManager *
testParallelDataTransfer(NSURL *baseURL)
{
NSURLSession *session;
NSURLSessionConfiguration *configuration;
URLManager *mgr;
NSURL *url;
const char *prefix = "<DataTransfer>";
NSInteger numberOfParallelTasks = 10;
/* URL Delegate Setup */
mgr = [URLManager new];
mgr->numberOfExpectedTasksBeforeCheck = numberOfParallelTasks;
expectedCountOfTasksToComplete += numberOfParallelTasks;
url = [baseURL URLByAppendingPathComponent:@"tmpRedirectToOK"];
configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
[configuration setHTTPMaximumConnectionsPerHost:0]; // Unlimited
session = [NSURLSession sessionWithConfiguration:configuration
delegate:mgr
delegateQueue:nil];
/* Setup Check Block */
[mgr setCheckBlock:^(URLManager *mgr) {
PASS_EQUAL(mgr->currentSession, session,
"%s URLManager Session is equal to session", prefix);
/* Check URLSession:didCreateTask: callback */
PASS(mgr->didCreateTaskCount == numberOfParallelTasks,
"%s didCreateTask: Count is correct", prefix);
/* Check URLSession:task:didCompleteWithError: */
PASS(nil == mgr->didCompleteError,
"%s didCompleteWithError: No error occurred", prefix)
PASS(mgr->didCompleteCount == numberOfParallelTasks,
"%s didCompleteWithError: Count is correct", prefix);
[countLock lock];
currentCountOfCompletedTasks += numberOfParallelTasks;
[countLock unlock];
}];
for (NSInteger i = 0; i < numberOfParallelTasks; i++)
{
NSURLSessionDataTask *task;
task = [session dataTaskWithURL:url];
[task resume];
}
return mgr;
}
static void
testDataTaskWithBlock(NSURL *baseURL)
{
NSURLSession *session;
NSURLSessionDataTask *task;
NSURL *url;
expectedCountOfTasksToComplete += 1;
url = [baseURL URLByAppendingPathComponent:@"contentOK"];
session = [NSURLSession sharedSession];
task = [session
dataTaskWithURL:url
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSString *string;
const char *prefix = "<DataTaskWithBlock>";
PASS(nil != data, "%s data in completion handler is not nil", prefix);
PASS(nil != response, "%s response is not nil", prefix);
PASS([response isKindOfClass:[NSHTTPURLResponse class]],
"%s response is an NSHTTPURLResponse", prefix);
PASS(nil == error, "%s error is nil", prefix);
string = [[NSString alloc] initWithData:data
encoding:NSASCIIStringEncoding];
PASS_EQUAL(string, @"Hello World!", "%s received data is correct",
prefix);
[string release];
[countLock lock];
currentCountOfCompletedTasks += 1;
[countLock unlock];
}];
[task resume];
}
static void
testDataTaskWithCookies(NSURL *baseURL)
{
NSURLSession *session;
NSURLSessionConfiguration *config;
NSURLSessionDataTask *task;
NSURL *url;
NSHTTPCookieStorage *cookies;
NSHTTPCookie *requestCookie;
expectedCountOfTasksToComplete += 1;
url = [baseURL URLByAppendingPathComponent:@"setCookiesOK"];
requestCookie = [NSHTTPCookie cookieWithProperties:requestCookieProperties];
cookies = [NSHTTPCookieStorage new];
[cookies setCookie:requestCookie];
config = [NSURLSessionConfiguration new];
[config setHTTPCookieAcceptPolicy:NSHTTPCookieAcceptPolicyAlways];
[config setHTTPCookieStorage:cookies];
[config setHTTPShouldSetCookies:YES];
session = [NSURLSession sessionWithConfiguration:config];
task = [session
dataTaskWithURL:url
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSString *string;
NSArray<NSHTTPCookie *> *cookieArray;
NSDate *date;
const char *prefix = "<DataTaskWithCookies>";
PASS(nil != data, "%s data in completion handler is not nil", prefix);
PASS(nil != response, "%s response is not nil", prefix);
PASS([response isKindOfClass:[NSHTTPURLResponse class]],
"%s response is an NSHTTPURLResponse", prefix);
PASS(nil == error, "%s error is nil", prefix);
string = [[NSString alloc] initWithData:data
encoding:NSASCIIStringEncoding];
PASS_EQUAL(string, @"Hello, world!", "%s received data is correct",
prefix);
cookieArray = [cookies cookiesForURL:url];
NSInteger count = 0;
for (NSHTTPCookie *ck in cookieArray)
{
if ([[ck name] isEqualToString:@"RequestCookie"])
{
PASS_EQUAL(ck, requestCookie, "RequestCookie is correct");
count += 1;
}
else if ([[ck name] isEqualToString:@"sessionId"])
{
date = [NSDate dateWithString:@"2100-06-09 10:18:14 +0000"];
PASS_EQUAL([ck name], @"sessionId", "Cookie name is correct");
PASS_EQUAL([ck value], @"abc123", "Cookie value is correct");
PASS([ck version] == 0, "Correct cookie version");
PASS([date isEqual:[ck expiresDate]],
"Cookie expiresDate is correct");
count += 1;
}
}
PASS(count == 2, "Found both cookies");
[string release];
[countLock lock];
currentCountOfCompletedTasks += 1;
[countLock unlock];
}];
[task resume];
}
/* Check if NSURLSessionTask correctly unfolds folded header lines */
static void
foldedHeaderDataTaskTest(NSURL *baseURL)
{
NSURLSession *session;
NSURLSessionDataTask *task;
NSURL *url;
expectedCountOfTasksToComplete += 1;
url = [baseURL URLByAppendingPathComponent:@"foldedHeaders"];
session = [NSURLSession sharedSession];
task = [session
dataTaskWithURL:url
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSHTTPURLResponse *urlResponse = (NSHTTPURLResponse *) response;
NSString *string;
NSDictionary *headerDict;
const char *prefix = "<DataTaskWithFoldedHeaders>";
headerDict = [urlResponse allHeaderFields];
PASS(nil != data, "%s data in completion handler is not nil", prefix);
PASS(nil != response, "%s response is not nil", prefix);
PASS([response isKindOfClass:[NSHTTPURLResponse class]],
"%s response is an NSHTTPURLResponse", prefix);
PASS(nil == error, "%s error is nil", prefix);
string = [[NSString alloc] initWithData:data
encoding:NSASCIIStringEncoding];
PASS_EQUAL(string, @"Hello World!", "%s received data is correct",
prefix);
PASS_EQUAL([headerDict objectForKey:@"Folded-Header-SP"], @"Testing",
"Folded header with continuation space is parsed correctly");
PASS_EQUAL([headerDict objectForKey:@"Folded-Header-TAB"], @"Testing",
"Folded header with continuation tab is parsed correctly");
[string release];
[countLock lock];
currentCountOfCompletedTasks += 1;
[countLock unlock];
}];
[task resume];
}
/* The disposition handler triggers transfer cancelation */
static void
testAbortAfterDidReceiveResponse(NSURL *baseURL)
{
NSURLSession *session;
NSURLSessionConfiguration *configuration;
NSURLSessionDataTask *task;
URLManager *mgr;
URLManagerCheckBlock block;
const char *prefix = "<AbortAfterDidReceiveResponseTest>";
NSURL *contentOKURL;
/* URL Delegate Setup */
mgr = [URLManager new];
mgr->numberOfExpectedTasksBeforeCheck = 1;
mgr->responseAnswer = NSURLSessionResponseCancel;
expectedCountOfTasksToComplete += 1;
/* URL Setup */
contentOKURL = [baseURL URLByAppendingPathComponent:@"contentOK"];
configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
session = [NSURLSession sessionWithConfiguration:configuration
delegate:mgr
delegateQueue:nil];
task = [session dataTaskWithURL:contentOKURL];
PASS(nil != task, "%s Session created a valid download task", prefix);
/* Setup Check Block */
block = dataCheckBlockFailedRequest(prefix, session, task);
[mgr setCheckBlock:block];
_Block_release(block);
[task resume];
}
int
main(int argc, char *argv[])
{
@autoreleasepool
{
NSBundle *bundle;
NSString *helperPath;
NSURL *baseURL;
NSFileManager *fm;
HTTPServer *server;
Class httpServerClass;
Class routeClass;
requestCookieProperties = @{
NSHTTPCookieName : @"RequestCookie",
NSHTTPCookieValue : @"1234",
NSHTTPCookieDomain : @"127.0.0.1",
NSHTTPCookiePath : @"/",
NSHTTPCookieExpires :
[NSDate dateWithString:@"2100-06-09 12:18:14 +0000"],
NSHTTPCookieSecure : @NO,
};
fm = [NSFileManager defaultManager];
helperPath = [[fm currentDirectoryPath]
stringByAppendingString:@"/Helpers/HTTPServer.bundle"];
countLock = [[NSLock alloc] init];
bundle = [NSBundle bundleWithPath:helperPath];
if (![bundle load])
{
[NSException raise:NSInternalInconsistencyException
format:@"failed to load HTTPServer.bundle"];
}
httpServerClass = [bundle principalClass];
routeClass = [bundle classNamed:@"Route"];
/* Bind to dynamic port. Set routes after initialisation. */
server = [[httpServerClass alloc] initWithPort:0 routes:nil];
if (!server)
{
[NSException raise:NSInternalInconsistencyException
format:@"failed to initialise HTTPServer"];
}
baseURL =
[NSURL URLWithString:[NSString stringWithFormat:@"http://127.0.0.1:%ld",
[server port]]];
NSLog(@"Test Server: baseURL=%@", baseURL);
[server setRoutes:createRoutes(routeClass, baseURL)];
[server resume];
// Call Test Functions here
testSimpleDownloadTransfer(baseURL);
testDownloadTransferWithBlock(baseURL);
testParallelDataTransfer(baseURL);
testDataTaskWithBlock(baseURL);
testDataTaskWithCookies(baseURL);
// Testing Header Line Unfolding
foldedHeaderDataTaskTest(baseURL);
// Redirects
testDownloadTransferWithRedirect(baseURL);
testDataTransferWithRedirectAndBlock(baseURL);
testDataTransferWithCanceledRedirect(baseURL);
testDataTransferWithRelativeRedirect(baseURL);
/* Abort in Delegate */
testAbortAfterDidReceiveResponse(baseURL);
[[NSRunLoop currentRunLoop]
runForSeconds:testTimeOut
conditionBlock:^BOOL(void) {
return expectedCountOfTasksToComplete != currentCountOfCompletedTasks;
}];
[server suspend];
PASS(expectedCountOfTasksToComplete == currentCountOfCompletedTasks,
"All transfers were completed before a timeout occurred");
[server release];
[countLock release];
}
}
#else
int
main(int argc, char *argv[])
{
return 0;
}
#endif /* GS_HAVE_NSURLSESSION */

View file

@ -0,0 +1,141 @@
#import <Foundation/Foundation.h>
#import "Testing.h"
#import "ObjectTesting.h"
#if GS_HAVE_NSURLSESSION
@interface MyDelegate : NSObject <NSURLSessionDelegate>
{
@public
BOOL responseCompletion;
BOOL didComplete;
NSString *taskText;
NSError *taskError;
}
@end
@implementation MyDelegate
- (void) dealloc
{
RELEASE(taskText);
RELEASE(taskError);
[super dealloc];
}
- (void) URLSession: (NSURLSession*)session
dataTask: (NSURLSessionDataTask*)dataTask
didReceiveResponse: (NSURLResponse*)response
completionHandler: (void (^)(NSURLSessionResponseDisposition disposition))completionHandler
{
responseCompletion = YES;
if (NO == didComplete)
{
NSLog(@"### handler 1 before didComplete...");
}
else
{
NSLog(@"### handler 1 after didComplete...");
}
completionHandler(NSURLSessionResponseAllow);
}
- (void) URLSession: (NSURLSession*)session
dataTask: (NSURLSessionDataTask*)dataTask
didReceiveData: (NSData*)data
{
NSString *text;
text = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];
if (nil == text)
{
NSLog(@"Received non-utf8 %@", data);
}
else
{
ASSIGN(taskText, text);
NSLog(@"Received String %@", text);
}
RELEASE(text);
}
- (void) URLSession: (NSURLSession*)session
task: (NSURLSessionTask*)task
didCompleteWithError: (NSError*)error
{
if (error == nil)
{
NSLog(@"Download is Succesfull");
}
else
{
NSLog(@"Error %@", [error userInfo]);
}
didComplete = YES;
ASSIGN(taskError, error);
}
@end
#endif
int main()
{
START_SET("NSURLSession test01")
#if !GS_HAVE_NSURLSESSION
SKIP("library built without NSURLSession support")
#else
NSURLSessionConfiguration *defaultConfigObject;
NSURLSession *defaultSession;
NSURLSessionDataTask *dataTask;
NSMutableURLRequest *urlRequest;
NSURL *url;
NSOperationQueue *mainQueue;
NSString *params;
MyDelegate *object;
#if defined(_WIN32)
NSLog(@"Marking nonexistant host test as hopeful on Windows as it seems to be broken");
testHopeful = YES;
#endif
object = AUTORELEASE([MyDelegate new]);
mainQueue = [NSOperationQueue mainQueue];
defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];
defaultSession = [NSURLSession sessionWithConfiguration: defaultConfigObject
delegate: object
delegateQueue: mainQueue];
url = [NSURL URLWithString:
@"http://localhost:12345/not-here"];
urlRequest = [NSMutableURLRequest requestWithURL: url];
[urlRequest setHTTPMethod: @"POST"];
params = @"name=Ravi&loc=India&age=31&submit=true";
[urlRequest setHTTPBody: [params dataUsingEncoding: NSUTF8StringEncoding]];
if ([urlRequest respondsToSelector: @selector(setDebug:)])
{
[urlRequest setDebug: YES];
}
dataTask = [defaultSession dataTaskWithRequest: urlRequest];
[dataTask resume];
NSDate *limit = [NSDate dateWithTimeIntervalSinceNow: 60.0];
while (object->didComplete == NO
&& [limit timeIntervalSinceNow] > 0.0)
{
ENTER_POOL
NSDate *when = [NSDate dateWithTimeIntervalSinceNow: 0.1];
[[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode
beforeDate: when];
LEAVE_POOL
}
PASS(YES == object->didComplete, "request completed")
PASS([object->taskError code] == NSURLErrorCannotConnectToHost,
"unable to connect to host")
#if defined(_WIN32)
testHopeful = NO;
#endif
#endif
END_SET("NSURLSession test01")
return 0;
}

View file

@ -0,0 +1,100 @@
#import <Foundation/Foundation.h>
#import "Testing.h"
#import "ObjectTesting.h"
#import "../NSURLConnection/Helpers/TestWebServer.h"
#if GS_HAVE_NSURLSESSION
#import "delegate.g"
#endif
int main()
{
START_SET("NSURLSession http")
#if !GS_HAVE_NSURLSESSION
SKIP("library built without NSURLSession support")
#else
NSFileManager *fm;
NSBundle *bundle;
NSString *helperPath;
// load the test suite's classes
fm = [NSFileManager defaultManager];
helperPath = [[fm currentDirectoryPath] stringByAppendingPathComponent:
@"../NSURLConnection/Helpers/TestConnection.bundle"];
bundle = [NSBundle bundleWithPath: helperPath];
NSCAssert([bundle load], NSInternalInconsistencyException);
TestWebServer *server;
Class c;
BOOL debug = YES;
// create a shared TestWebServer instance for performance
c = [bundle principalClass];
server = [[c testWebServerClass] new];
NSCAssert(server != nil, NSInternalInconsistencyException);
[server setDebug: debug];
[server start: nil]; // localhost:1234 HTTP
NSURLSessionConfiguration *configuration;
NSURLSession *defaultSession;
NSURLSessionDataTask *dataTask;
NSMutableURLRequest *urlRequest;
NSURL *url;
NSOperationQueue *mainQueue;
NSString *params;
MyDelegate *object;
configuration = [[NSURLSessionConfiguration alloc] init];
[configuration setHTTPShouldUsePipelining: YES];
testHopeful=YES;
PASS_RUNS([configuration setHTTPMaximumConnectionLifetime: 42];,
"-setHTTPMaximumConnectionLifetime: support available in CURL")
testHopeful=NO;
[configuration setHTTPMaximumConnectionsPerHost: 1];
[configuration setRequestCachePolicy: NSURLCacheStorageNotAllowed];
object = AUTORELEASE([MyDelegate new]);
mainQueue = [NSOperationQueue mainQueue];
defaultSession = [NSURLSession sessionWithConfiguration: configuration
delegate: object
delegateQueue: mainQueue];
RELEASE(configuration);
url = [NSURL URLWithString: @"http://localhost:1234/xxx"];
params = @"dummy=true";
urlRequest = [NSMutableURLRequest requestWithURL: url];
[urlRequest setHTTPMethod: @"POST"];
[urlRequest setHTTPBody: [params dataUsingEncoding: NSUTF8StringEncoding]];
if ([urlRequest respondsToSelector: @selector(setDebug:)])
{
[urlRequest setDebug: YES];
}
dataTask = [defaultSession dataTaskWithRequest: urlRequest];
[dataTask resume];
NSDate *limit = [NSDate dateWithTimeIntervalSinceNow: 60.0];
while ([object finished] == NO
&& [limit timeIntervalSinceNow] > 0.0)
{
ENTER_POOL
NSDate *when = [NSDate dateWithTimeIntervalSinceNow: 0.1];
[[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode
beforeDate: when];
LEAVE_POOL
}
PASS(YES == [object finished], "request completed")
PASS_EQUAL([object taskError], nil, "request did not error")
NSString *expect = @"Please give login and password";
PASS_EQUAL([object taskText], expect, "request returned text")
#endif
END_SET("NSURLSession http")
return 0;
}

View file

@ -0,0 +1,94 @@
#import <Foundation/Foundation.h>
#import "Testing.h"
#import "ObjectTesting.h"
#import "../NSURLConnection/Helpers/TestWebServer.h"
#if GS_HAVE_NSURLSESSION
#import "delegate.g"
#endif
int main()
{
START_SET("NSURLSession test03")
#if !GS_HAVE_NSURLSESSION
SKIP("library built without NSURLSession support")
#else
NSFileManager *fm;
NSBundle *bundle;
NSString *helperPath;
// load the test suite's classes
fm = [NSFileManager defaultManager];
helperPath = [[fm currentDirectoryPath] stringByAppendingPathComponent:
@"../NSURLConnection/Helpers/TestConnection.bundle"];
bundle = [NSBundle bundleWithPath: helperPath];
NSCAssert([bundle load], NSInternalInconsistencyException);
TestWebServer *server;
Class c;
BOOL debug = YES;
// create a shared TestWebServer instance for performance
c = [bundle principalClass];
server = [[c testWebServerClass] new];
NSCAssert(server != nil, NSInternalInconsistencyException);
[server setDebug: debug];
[server start: nil]; // localhost:1234 HTTP
NSURLSessionConfiguration *configuration;
NSURLSession *defaultSession;
NSURLSessionDownloadTask *downloadTask;
NSMutableURLRequest *urlRequest;
NSURL *url;
NSOperationQueue *mainQueue;
MyDelegate *object;
NSString *content;
configuration = [[NSURLSessionConfiguration alloc] init];
object = AUTORELEASE([MyDelegate new]);
mainQueue = [NSOperationQueue mainQueue];
defaultSession = [NSURLSession sessionWithConfiguration: configuration
delegate: object
delegateQueue: mainQueue];
RELEASE(configuration);
url = [NSURL URLWithString: @"http://localhost:1234/index"];
urlRequest = [NSMutableURLRequest requestWithURL: url];
if ([urlRequest respondsToSelector: @selector(setDebug:)])
{
[urlRequest setDebug: YES];
}
downloadTask = [defaultSession downloadTaskWithRequest: urlRequest];
[downloadTask resume];
NSDate *limit = [NSDate dateWithTimeIntervalSinceNow: 60.0];
while ([object finished] == NO
&& [limit timeIntervalSinceNow] > 0.0)
{
ENTER_POOL
NSDate *when = [NSDate dateWithTimeIntervalSinceNow: 0.1];
[[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode
beforeDate: when];
LEAVE_POOL
}
PASS(YES == [object finished], "request completed")
PASS_EQUAL([object taskError], nil, "request did not error")
/* Get content from file */
content = [NSString stringWithContentsOfFile: [[object taskLocation] path]
encoding: NSUTF8StringEncoding
error:nil];
NSString *expect = @"Please give login and password";
PASS_EQUAL(content, expect, "request returned text")
#endif
END_SET("NSURLSession test03")
return 0;
}

View file

@ -1,204 +0,0 @@
#include "Foundation/NSDate.h"
#import <Foundation/Foundation.h>
#include <Foundation/NSProgress.h>
#include <Foundation/NSString.h>
#if GS_HAVE_NSURLSESSION
#import "Helpers/HTTPServer.h"
#import "NSRunLoop+TimeOutAdditions.h"
#import "URLManager.h"
#import "Testing.h"
typedef void (^dataCompletionHandler)(NSData *data, NSURLResponse *response,
NSError *error);
/* Timeout in Seconds */
static NSInteger testTimeOut = 60;
static NSTimeInterval expectedCountOfTasksToComplete = 0;
/* Accessed in delegate on different thread.
*/
static _Atomic(NSInteger) currentCountOfCompletedTasks = 0;
static NSLock *countLock;
/* Expected Content */
static NSString *largeBodyPath;
static NSData *largeBodyContent;
static NSArray<Route *> *
createRoutes(Class routeClass)
{
Route *routeOKWithContent;
Route *routeLargeUpload;
NSURL *routeOKWithContentURL;
NSURL *routeLargeUploadURL;
routeOKWithContentURL = [NSURL URLWithString:@"/smallUploadOK"];
routeLargeUploadURL = [NSURL URLWithString:@"/largeUploadOK"];
routeOKWithContent = [routeClass
routeWithURL:routeOKWithContentURL
method:@"POST"
handler:^NSData *(NSURLRequest *req) {
NSData *response;
response =
[@"HTTP/1.1 200 OK\r\nContent-Length: 0\r\nHeader-Key: "
@"Header-Value\r\n\r\n" dataUsingEncoding:NSASCIIStringEncoding];
return response;
}];
routeLargeUpload = [routeClass
routeWithURL:routeLargeUploadURL
method:@"POST"
handler:^NSData *(NSURLRequest *req) {
NSData *response;
PASS_EQUAL([req valueForHTTPHeaderField:@"Request-Key"],
@"Request-Value",
"Request contains user-specific header line");
PASS_EQUAL([req valueForHTTPHeaderField:@"Content-Type"],
@"text/plain",
"Request contains the correct Content-Type");
PASS_EQUAL([req HTTPBody], largeBodyContent, "HTTPBody is correct");
response =
[@"HTTP/1.1 200 OK\r\nContent-Length: 0\r\nHeader-Key: "
@"Header-Value\r\n\r\n" dataUsingEncoding:NSASCIIStringEncoding];
return response;
}];
return @[ routeOKWithContent, routeLargeUpload ];
}
static void
testLargeUploadWithBlock(NSURL *baseURL)
{
NSURLSession *session;
NSURLSessionDataTask *dataTask;
NSURLSessionUploadTask *uploadTask;
NSMutableURLRequest *request;
NSURL *url;
dataCompletionHandler handler;
expectedCountOfTasksToComplete += 2;
url = [baseURL URLByAppendingPathComponent:@"largeUploadOK"];
session = [NSURLSession sharedSession];
request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPBody:largeBodyContent];
[request setHTTPMethod:@"POST"];
[request setValue:@"Request-Value" forHTTPHeaderField:@"Request-Key"];
[request setValue:@"text/plain" forHTTPHeaderField:@"Content-Type"];
/* The completion handler for the two requests */
handler = ^(NSData *data, NSURLResponse *response, NSError *error) {
NSHTTPURLResponse *httpResponse = response;
PASS([data length] == 0, "Received empty data object");
PASS(nil != response, "Response is not nil");
PASS([response isKindOfClass:[NSHTTPURLResponse class]],
"Response is a NSHTTPURLResponse");
PASS(nil == error, "Error is nil");
PASS_EQUAL([[httpResponse allHeaderFields] objectForKey:@"Header-Key"],
@"Header-Value", "Response contains custom header line");
[countLock lock];
currentCountOfCompletedTasks += 1;
[countLock unlock];
};
dataTask = [session dataTaskWithRequest:request completionHandler:handler];
uploadTask = [session uploadTaskWithRequest:request
fromData:largeBodyContent
completionHandler:handler];
[dataTask resume];
[uploadTask resume];
}
int
main(int argc, char *argv[])
{
@autoreleasepool
{
NSBundle *bundle;
NSString *helperPath;
NSString *currentDirectory;
NSURL *baseURL;
NSFileManager *fm;
NSArray<Route *> *routes;
HTTPServer *server;
Class httpServerClass;
Class routeClass;
fm = [NSFileManager defaultManager];
currentDirectory = [fm currentDirectoryPath];
helperPath =
[currentDirectory stringByAppendingString:@"/Helpers/HTTPServer.bundle"];
countLock = [[NSLock alloc] init];
bundle = [NSBundle bundleWithPath:helperPath];
if (![bundle load])
{
[NSException raise:NSInternalInconsistencyException
format:@"failed to load HTTPServer.bundle"];
}
/* Load Test Data */
largeBodyPath =
[currentDirectory stringByAppendingString:@"/Resources/largeBody.txt"];
largeBodyContent = [NSData dataWithContentsOfFile:largeBodyPath];
PASS(nil != largeBodyContent, "can load %s", [largeBodyPath UTF8String]);
httpServerClass = [bundle principalClass];
routeClass = [bundle classNamed:@"Route"];
routes = createRoutes(routeClass);
server = [[httpServerClass alloc] initWithPort:0 routes:routes];
if (!server)
{
[NSException raise:NSInternalInconsistencyException
format:@"failed to create HTTPServer"];
}
baseURL =
[NSURL URLWithString:[NSString stringWithFormat:@"http://127.0.0.1:%ld",
[server port]]];
NSLog(@"Server started with baseURL: %@", baseURL);
[server resume];
/* Call Test Functions here! */
testLargeUploadWithBlock(baseURL);
[[NSRunLoop currentRunLoop]
runForSeconds:testTimeOut
conditionBlock:^BOOL(void) {
return expectedCountOfTasksToComplete != currentCountOfCompletedTasks;
}];
[server suspend];
PASS(expectedCountOfTasksToComplete == currentCountOfCompletedTasks,
"All transfers were completed before a timeout occurred");
[server release];
[countLock release];
}
return 0;
}
#else
int
main(int argc, char *argv[])
{
return 0;
}
#endif