mirror of
https://github.com/gnustep/libs-base.git
synced 2025-04-23 17:10:48 +00:00
Add NSURLConnection patch by Sergei Golovin
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@38214 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
parent
a75ab40fec
commit
7ecc487a1b
21 changed files with 4457 additions and 27 deletions
|
@ -1,3 +1,8 @@
|
|||
2014-11-29 Sergei Golovin <Golovin.SV@gmail.com>
|
||||
|
||||
* Tests/base/NSURLConnection: Test helper tool plus a load of
|
||||
tests for NSURLConnection.
|
||||
|
||||
2014-11-29 Richard Frith-Macdonald <rfm@gnu.org>
|
||||
|
||||
* Source/NSScanner.m: Fixup error in scanning doubles with excess
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
# __GENERATED__ makefile marker
|
||||
#
|
||||
|
||||
include $(GNUSTEP_MAKEFILES)/common.make
|
||||
-include ../GNUmakefile.super
|
||||
|
||||
GNUSTEP_OBJ_DIR=./obj
|
||||
|
||||
TEST_TOOL_NAME = basic
|
||||
|
||||
ifeq ($(gcov),yes)
|
||||
ADDITIONAL_OBJCFLAGS += -ftest-coverage -fprofile-arcs
|
||||
ADDITIONAL_OBJCCFLAGS += -ftest-coverage -fprofile-arcs
|
||||
ADDITIONAL_LDFLAGS += -ftest-coverage -fprofile-arcs
|
||||
ADDITIONAL_TOOL_LIBS+=-lgcov
|
||||
endif
|
||||
|
||||
|
||||
basic_OBJC_FILES=basic.m
|
||||
|
||||
-include GNUmakefile.preamble
|
||||
include $(GNUSTEP_MAKEFILES)/test-tool.make
|
||||
-include GNUmakefile.postamble
|
||||
|
||||
after-clean::
|
||||
rm -f core core.* *.core tests.log tests.sum oldtests.log oldtests.sum
|
||||
|
20
Tests/base/NSURLConnection/Helpers/GNUmakefile
Normal file
20
Tests/base/NSURLConnection/Helpers/GNUmakefile
Normal file
|
@ -0,0 +1,20 @@
|
|||
include $(GNUSTEP_MAKEFILES)/common.make
|
||||
|
||||
BUNDLE_NAME = TestConnection
|
||||
|
||||
TestConnection_OBJC_FILES = TestCase.m SimpleWebServer.m TestWebServer.m NSURLConnectionTest.m RequestHandler.m
|
||||
#TestConnection_OBJC_LIBS += -lWebServer -lPerformance
|
||||
TestConnection_RESOURCE_FILES += testKey.pem testCert.pem
|
||||
TestConnection_PRINCIPAL_CLASS = NSURLConnectionTest
|
||||
|
||||
StatusServer_NEEDS_GUI = NO
|
||||
|
||||
TOOL_NAME = testTestWebServer
|
||||
testTestWebServer_OBJC_FILES += testTestWebServer.m
|
||||
testTestWebServer_NEEDS_GUI = NO
|
||||
|
||||
-include GNUmakefile.preamble
|
||||
include $(GNUSTEP_MAKEFILES)/bundle.make
|
||||
include $(GNUSTEP_MAKEFILES)/tool.make
|
||||
-include GNUmakefile.postamble
|
||||
|
219
Tests/base/NSURLConnection/Helpers/NSURLConnectionTest.h
Normal file
219
Tests/base/NSURLConnection/Helpers/NSURLConnectionTest.h
Normal file
|
@ -0,0 +1,219 @@
|
|||
/** -*- objc -*-
|
||||
*
|
||||
* Author: Sergei Golovin <Golovin.SV@gmail.com>
|
||||
*
|
||||
* The class is intended to test the class NSURLConnection. It is designed to start
|
||||
* a TestWebServer instance and make NSURLConnection to it getting TestWebServerDelegate's
|
||||
* and NSURLConnection's callbacks. As TestCase's child it has two flag sets, the actual
|
||||
* one and the reference one (See TestCase.h). It sets/unsets flags of the actual set on
|
||||
* the execution path at the 'checkpoints' listed below as macros.
|
||||
*
|
||||
* The method -[isSuccess] is called when the NSURLConnection finishes (fails). It makes
|
||||
* a comparison between the two flag sets. The result signifies a success/failure of the
|
||||
* test.
|
||||
*
|
||||
* The test case which the NSURLConnectionTest implements by default is connecting
|
||||
* to the http://localhost:54321/ whith the HTTP method 'GET'. You can change variable
|
||||
* parts of process by supplying a custom dictionary to the method -[setUpTest:]
|
||||
* before the test case is started by a call of the -[startTest:]. The use pattern:
|
||||
* --------------------------------------------------------------------------
|
||||
* #import "NSURLConnectionTest.h"
|
||||
* #import "Testing.h"
|
||||
*
|
||||
* NSURLConnectionTest *testCase = [NSURLConnectionTest new];
|
||||
* // the extra dictionary with test cases's parameters
|
||||
* NSDictionary *d = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
* @"/somepath", @"Path",
|
||||
* @"POST", @"Method",
|
||||
* @"https", @"Protocol",
|
||||
* nil];
|
||||
* [testCase setUpTest: d];
|
||||
* [testCase startTest: d];
|
||||
* PASS([testCase isSuccess], "a diagnostic message about the test");
|
||||
* [testCase tearDownTest: d];
|
||||
* DESTROY(testCase);
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* The method -[setUpTest:] recognises the following variable parts (supplied as key-value
|
||||
* pairs):
|
||||
*
|
||||
* 'Instance'
|
||||
* 'AuxInstance' - holds an already running main/auxilliary TestWebServer instance.
|
||||
* If nil the class will run one main instance of TestWebServer
|
||||
* and no auxilliary one. The key 'IsAuxilliary' with the value 'YES'
|
||||
* should be supplied to run an own auxilliary instance.
|
||||
* Useful to run several tests consequently. The test won't stop
|
||||
* on its own the supplied instance(s). It leaves the calling code
|
||||
* to make the decision. The auxilliary instance 'AuxInstance' is
|
||||
* used in tests on redirecting where two web servers are needed.
|
||||
* Default: nil
|
||||
* 'IsAuxilliary' - whether the NSURLConnectionTest should run it's own auxilliary
|
||||
* TestWebServer instance. You must supply YES as a value to run it.
|
||||
* It will be run with the same parameters (address/protocol/detached)
|
||||
* as the main one except of the port which is assigned from the value
|
||||
* of the key 'AuxPort'.
|
||||
* Default: NO
|
||||
* 'IsDetached' - whether to run the own(and auxilliary) instance in the detached mode
|
||||
* (the instance will be run within a detached thread).
|
||||
* Default: NO
|
||||
* 'Address' - the address of the remote side.
|
||||
* Default: localhost
|
||||
* 'Port' - the port of the remote side.
|
||||
* Default: 54321
|
||||
* 'AuxPort' - the port of the auxilliary remote side (where the connection
|
||||
* to be redirected).
|
||||
* Default: 54322
|
||||
* 'Protocol' - the network protocol (supports currently http, https).
|
||||
* Default: http
|
||||
* 'Path' - the path of the URL.
|
||||
* Default: '/'
|
||||
* 'RedirectPath' - the path where request should be redirected in corresponding tests.
|
||||
* Default: '/'
|
||||
* 'StatusCode' - the status code expected from the remote side if the test
|
||||
* is successful.
|
||||
* Default: 204
|
||||
* 'RedirectCode' - the status code expected from the remote side on the connection's first
|
||||
* stage in a test with redirect.
|
||||
* Default: 301
|
||||
* 'Method' - the request's method.
|
||||
* Default: GET
|
||||
* 'Payload' - the request's payload. It can be of NSString or of NSData class.
|
||||
* The class produces NSData from NSString using NSUTF8StringEncoding.
|
||||
* Default: nil
|
||||
* 'Content' - the expected content. It can be of NSString or of NSData class.
|
||||
* The class produces NSData from NSString using NSUTF8StringEncoding.
|
||||
* Default: nil
|
||||
* 'ReferenceFlags' - the dictionary to correct the guessed reference flag set.
|
||||
* Default: nil
|
||||
* The class tries to infer the reference flag set from
|
||||
* the test request. If that guessed (default) set is wrong then this
|
||||
* dictionary allows to correct it.
|
||||
* The class recognises the following keys:
|
||||
*
|
||||
* 'SENTREQUEST'
|
||||
* 'AUTHORIZED'
|
||||
* 'NOTAUTHORIZED'
|
||||
* 'GOTUNAUTHORIZED'
|
||||
* 'GOTREQUEST'
|
||||
* 'SENTRESPONSE'
|
||||
* 'GOTRESPONSE'
|
||||
* 'GOTCONTENT'
|
||||
* 'GOTFINISH'
|
||||
* 'GOTFAIL'
|
||||
* 'GOTREDIRECT'
|
||||
*
|
||||
* the 'YES' as a value means the corresponding reference flag
|
||||
* must be set, otherwise 'NO' means it must not be set.
|
||||
*
|
||||
* The NSURLConnectionTest can raise it's verbosity level by the method call -[setDebug: YES].
|
||||
* In this case whole connection session will be showed in the log.
|
||||
*
|
||||
*/
|
||||
|
||||
#import "TestWebServer.h"
|
||||
#import "TestCase.h"
|
||||
|
||||
/* the test's checkpoint list */
|
||||
|
||||
/* the request has been sent by the client and reaches the server */
|
||||
#define SENTREQUEST 1
|
||||
/* the client's request has been authorized */
|
||||
#define AUTHORIZED 2
|
||||
/* the client's request hasn't been authorized */
|
||||
#define NOTAUTHORIZED 4
|
||||
/* the client has got Unauthorized response */
|
||||
#define GOTUNAUTHORIZED 8
|
||||
/* the server has got a right request */
|
||||
#define GOTREQUEST 16
|
||||
/* the server is ready to send the response */
|
||||
#define SENTRESPONSE 32
|
||||
/* the client has got the response from the server with valid headers/properties */
|
||||
#define GOTRESPONSE 64
|
||||
/* the client has got the expected content from the server's response */
|
||||
#define GOTCONTENT 128
|
||||
/* the test's execution has reached client's -[connectionDidFinishLoading:] */
|
||||
#define GOTFINISH 256
|
||||
/* the test's execution has reached client's -[connection:didFailWithError:] */
|
||||
#define GOTFAIL 512
|
||||
/* the test's execution has reached client's -[connection:willSendRequest:redirectResponse:]*/
|
||||
#define GOTREDIRECT 1024
|
||||
/* the end of the test's checkpoint list */
|
||||
|
||||
@interface NSURLConnectionTest : TestCase <TestWebServerDelegate>
|
||||
{
|
||||
/* the main TestWebServer instance */
|
||||
TestWebServer *_server;
|
||||
/* tha auxilliary TestWebServer instance needed in tests on redirecting */
|
||||
TestWebServer *_auxServer;
|
||||
/* the custom request (made by the instance or supplied externally) */
|
||||
NSURLRequest *_request;
|
||||
/* the redirect request (made by the instance) */
|
||||
NSURLRequest *_redirectRequest;
|
||||
/* the data accumulator for the received response's content */
|
||||
NSMutableData *_received;
|
||||
/* the expected status code */
|
||||
NSUInteger _expectedStatusCode;
|
||||
/* the expected redirect status code of the connection's first stage
|
||||
* in tests with redirect... the resulting (on the second stage)
|
||||
* expected status code is stored in the ivar _expectedStatusCode */
|
||||
NSUInteger _redirectStatusCode;
|
||||
/* the expected response's content */
|
||||
NSData *_expectedContent;
|
||||
/* the connection */
|
||||
NSURLConnection *_conn;
|
||||
/* to store the error supplied with the -[connection:didFailWithError:] */
|
||||
NSError *_error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a TestWebServer-like class used by this test case class. A descendant can
|
||||
* return more advanced implementation of TestWebServer's functionality.
|
||||
*/
|
||||
+ (Class)testWebServerClass;
|
||||
|
||||
+ (void)initialize;
|
||||
|
||||
- (id)init;
|
||||
|
||||
/* See the super's description */
|
||||
- (void)setUpTest:(id)extra;
|
||||
- (void)startTest:(id)extra;
|
||||
- (void)tearDownTest:(id)extra;
|
||||
|
||||
/**
|
||||
* The only difference from the super's one is issuing of a diagnostic message
|
||||
* if the test is failed.
|
||||
*/
|
||||
- (BOOL)isSuccess;
|
||||
|
||||
/**
|
||||
* Issues log messages describing the actual and reference flag sets' states.
|
||||
*/
|
||||
- (void)logFlags;
|
||||
|
||||
/**
|
||||
* Returns the error stored by the -[connection:didFailWithError:].
|
||||
*/
|
||||
- (NSError *)error;
|
||||
|
||||
/* NSURLConnectionDelegate */
|
||||
- (NSURLRequest *)connection:(NSURLConnection *)connection
|
||||
willSendRequest:(NSURLRequest *)request
|
||||
redirectResponse:(NSURLResponse *)redirectResponse;
|
||||
|
||||
- (void)connection:(NSURLConnection *)connection
|
||||
didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;
|
||||
|
||||
- (void)connection:(NSURLConnection *)connection
|
||||
didReceiveResponse:(NSURLResponse *)response;
|
||||
|
||||
- (void)connection:(NSURLConnection *)connection
|
||||
didReceiveData:(NSData *)data;
|
||||
|
||||
- (void)connectionDidFinishLoading:(NSURLConnection *)connection;
|
||||
|
||||
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error;
|
||||
|
||||
/* end of NSURLConnectionDelegate */
|
||||
|
||||
@end /* NSURLConnectionTest */
|
784
Tests/base/NSURLConnection/Helpers/NSURLConnectionTest.m
Normal file
784
Tests/base/NSURLConnection/Helpers/NSURLConnectionTest.m
Normal file
|
@ -0,0 +1,784 @@
|
|||
/*
|
||||
* Author: Sergei Golovin <Golovin.SV@gmail.com>
|
||||
*/
|
||||
|
||||
#import "NSURLConnectionTest.h"
|
||||
|
||||
/* the runloop's time slice */
|
||||
#define TIMING 0.1
|
||||
/* the max duration of a test */
|
||||
#define MAXDURATION 3.0
|
||||
|
||||
@interface NSURLConnectionTest (Private)
|
||||
|
||||
/**
|
||||
* The method tries to guess the request which the test case should make.
|
||||
* It recognizes as an argument an NSDictionary with custom variable parts
|
||||
* of connecting process (see the class's description). It also can be supplied
|
||||
* with the exact request which must be made during test execution.
|
||||
* The result of the method is stored in the ivar _request.
|
||||
*/
|
||||
- (void)_makeRequest:(id)extra;
|
||||
|
||||
/**
|
||||
* The method analyzes the ivar _request and tries to guess the execution path
|
||||
* inferring from it the reference flag set. If there is a deviation from the guessed
|
||||
* execution path the caller can supply NSDictionary as the argument 'extra' with
|
||||
* the key 'ReferenceFlags' is set to have a custom NSDictionary as a value explicitly
|
||||
* setting the particular flags (see the class's description).
|
||||
*/
|
||||
- (void)_makeReferenceFlags:(id)extra;
|
||||
|
||||
@end /* NSURLConnectionTest (Private) */
|
||||
|
||||
@implementation NSURLConnectionTest
|
||||
|
||||
+ (Class)testWebServerClass
|
||||
{
|
||||
return [TestWebServer class];
|
||||
}
|
||||
|
||||
/* The flag map connects flags's string names with flags... used to log actions */
|
||||
static NSMapTable *_flagMap = nil;
|
||||
|
||||
+ (void)initialize
|
||||
{
|
||||
if(nil == _flagMap)
|
||||
{
|
||||
_flagMap = NSCreateMapTable(NSObjectMapKeyCallBacks,
|
||||
NSIntegerMapValueCallBacks,
|
||||
0);
|
||||
NSMapInsert(_flagMap, @"SENTREQUEST", (void *)1);
|
||||
NSMapInsert(_flagMap, @"AUTHORIZED", (void *)2);
|
||||
NSMapInsert(_flagMap, @"NOTAUTHORIZED", (void *)4);
|
||||
NSMapInsert(_flagMap, @"GOTUNAUTHORIZED", (void *)8);
|
||||
NSMapInsert(_flagMap, @"GOTREQUEST", (void *)16);
|
||||
NSMapInsert(_flagMap, @"SENTRESPONSE", (void *)32);
|
||||
NSMapInsert(_flagMap, @"GOTRESPONSE", (void *)64);
|
||||
NSMapInsert(_flagMap, @"GOTCONTENT", (void *)128);
|
||||
NSMapInsert(_flagMap, @"GOTFINISH", (void *)256);
|
||||
NSMapInsert(_flagMap, @"GOTFAIL", (void *)512);
|
||||
NSMapInsert(_flagMap, @"GOTREDIRECT", (void *)1024);
|
||||
}
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
if((self = [super init]) != nil)
|
||||
{
|
||||
_conn = nil;
|
||||
_error = nil;
|
||||
_server = nil;
|
||||
_auxServer = nil;
|
||||
_received = nil;
|
||||
_request = nil;
|
||||
_redirectRequest = nil;
|
||||
_expectedStatusCode = 204;
|
||||
_expectedContent = nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
// super's -[dealloc] calls the -[tearDownTest:]
|
||||
// so place clearing there
|
||||
|
||||
- (void)setUpTest:(id)extra
|
||||
{
|
||||
[super setUpTest: extra];
|
||||
|
||||
[self _makeRequest: extra];
|
||||
[self _makeReferenceFlags: extra];
|
||||
_received = [NSMutableData new];
|
||||
}
|
||||
|
||||
- (void)startTest:(id)extra
|
||||
{
|
||||
CREATE_AUTORELEASE_POOL(arp);
|
||||
NSTimeInterval duration = 0.0;
|
||||
|
||||
if(YES == _debug)
|
||||
{
|
||||
NSLog(@"%@: started with request:\n%@", self, _request);
|
||||
[self logFlags];
|
||||
}
|
||||
|
||||
_conn = [[NSURLConnection alloc] initWithRequest: _request
|
||||
delegate: self];
|
||||
|
||||
while(!_done && !_failed)
|
||||
{
|
||||
[[NSRunLoop currentRunLoop]
|
||||
runUntilDate: [NSDate dateWithTimeIntervalSinceNow: TIMING]];
|
||||
duration += TIMING;
|
||||
if(duration >= MAXDURATION)
|
||||
{
|
||||
_failed = YES;
|
||||
}
|
||||
}
|
||||
if(YES == _debug)
|
||||
{
|
||||
NSLog(@"%@: stopped with request:\n%@", self, _request);
|
||||
[self logFlags];
|
||||
}
|
||||
DESTROY(arp);
|
||||
}
|
||||
|
||||
- (void)tearDownTest:(id)extra
|
||||
{
|
||||
BOOL isToStop = YES; // whether to stop the main TestWebServer instance
|
||||
BOOL isToStopAux = YES; // whether to stop the auxilliary TestWebServer instance
|
||||
if([_extra isKindOfClass: [NSDictionary class]])
|
||||
{
|
||||
NSDictionary *d = _extra;
|
||||
isToStop = ([d objectForKey: @"Instance"] == nil);
|
||||
isToStopAux = ([d objectForKey: @"AuxInstance"] == nil &&
|
||||
[[d objectForKey: @"IsAuxilliary"] isEqualToString: @"YES"]);
|
||||
}
|
||||
|
||||
if(isToStop)
|
||||
{
|
||||
if(nil != _server)
|
||||
{
|
||||
[_server stop];
|
||||
}
|
||||
}
|
||||
if(isToStopAux)
|
||||
{
|
||||
if(nil != _auxServer)
|
||||
{
|
||||
[_auxServer stop];
|
||||
}
|
||||
}
|
||||
|
||||
DESTROY(_server);
|
||||
DESTROY(_auxServer);
|
||||
DESTROY(_received);
|
||||
DESTROY(_request);
|
||||
DESTROY(_redirectRequest);
|
||||
DESTROY(_expectedContent);
|
||||
[_conn cancel];
|
||||
DESTROY(_conn);
|
||||
DESTROY(_error);
|
||||
|
||||
[super tearDownTest: extra];
|
||||
}
|
||||
|
||||
- (BOOL)isSuccess
|
||||
{
|
||||
BOOL ret = [super isSuccess];
|
||||
|
||||
|
||||
if(!ret)
|
||||
{
|
||||
/* log the flags that differ */
|
||||
NSString *key;
|
||||
NSUInteger flag;
|
||||
NSMapEnumerator en = NSEnumerateMapTable(_flagMap);
|
||||
while(NSNextMapEnumeratorPair(&en, (void **)&key, (void **)&flag))
|
||||
{
|
||||
if([self isReferenceFlagSet: flag] != [self isFlagSet: flag])
|
||||
{
|
||||
NSLog(@"ERR: %@", key);
|
||||
}
|
||||
}
|
||||
NSEndMapTableEnumeration(&en);
|
||||
|
||||
if(_failed)
|
||||
{
|
||||
NSLog(@"FAILED for unknown reason possibly not related to the test (e.g. a timer has expired)");
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
- (void)logFlags
|
||||
{
|
||||
NSString *value;
|
||||
NSString *key;
|
||||
NSUInteger flag;
|
||||
NSMapEnumerator en = NSEnumerateMapTable(_flagMap);
|
||||
while(NSNextMapEnumeratorPair(&en, (void **)&key, (void **)&flag))
|
||||
{
|
||||
if([self isFlagSet: flag])
|
||||
{
|
||||
value = @"YES";
|
||||
}
|
||||
else
|
||||
{
|
||||
value = @"NO";
|
||||
}
|
||||
|
||||
NSLog(@"ACTUAL: %@ -> %@", key, value);
|
||||
|
||||
if([self isReferenceFlagSet: flag])
|
||||
{
|
||||
value = @"YES";
|
||||
}
|
||||
else
|
||||
{
|
||||
value = @"NO";
|
||||
}
|
||||
|
||||
NSLog(@"REFERENCE: %@ -> %@", key, value);
|
||||
}
|
||||
NSEndMapTableEnumeration(&en);
|
||||
}
|
||||
|
||||
- (NSError *)error
|
||||
{
|
||||
return _error;
|
||||
}
|
||||
|
||||
/* TestWebServerDelegate */
|
||||
- (void)handler:(id)handler
|
||||
gotRequest:(GSMimeDocument *)request
|
||||
with:(TestWebServer *)server
|
||||
{
|
||||
NSString *method = [_request HTTPMethod];
|
||||
NSData *content = [request convertToData];
|
||||
|
||||
[self setFlags: SENTREQUEST];
|
||||
if(YES == _debug)
|
||||
{
|
||||
NSLog(@"%@: set SENTREQUEST (-[%@])", self, NSStringFromSelector(_cmd));
|
||||
}
|
||||
|
||||
// TODO: more comparisons of _request and request
|
||||
if([method isEqualToString: @"POST"] ||
|
||||
[method isEqualToString: @"PUT"])
|
||||
{
|
||||
if([content isEqualToData: [_request HTTPBody]])
|
||||
{
|
||||
[self setFlags: GOTREQUEST];
|
||||
if(YES == _debug)
|
||||
{
|
||||
NSLog(@"%@: set GOTREQUEST (-[%@])", self, NSStringFromSelector(_cmd));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
[self setFlags: GOTREQUEST];
|
||||
if(YES == _debug)
|
||||
{
|
||||
NSLog(@"%@: set GOTREQUEST (-[%@])", self, NSStringFromSelector(_cmd));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)handler:(id)handler
|
||||
willSendUnauthorized:(GSMimeDocument *)response
|
||||
with:(TestWebServer *)server
|
||||
{
|
||||
[self setFlags: NOTAUTHORIZED];
|
||||
if(YES == _debug)
|
||||
{
|
||||
NSLog(@"%@: set NOTAUTHORIZED (-[%@])", self, NSStringFromSelector(_cmd));
|
||||
}
|
||||
}
|
||||
|
||||
- (void)handler:(id)handler
|
||||
gotAuthorized:(GSMimeDocument *)request
|
||||
with:(TestWebServer *)server
|
||||
{
|
||||
[self setFlags: AUTHORIZED];
|
||||
if(YES == _debug)
|
||||
{
|
||||
NSLog(@"%@: set AUTHORIZED (-[%@])", self, NSStringFromSelector(_cmd));
|
||||
}
|
||||
}
|
||||
|
||||
- (void)handler:(id)handler
|
||||
willSend:(GSMimeDocument *)response
|
||||
with:(TestWebServer *)server
|
||||
{
|
||||
[self setFlags: SENTRESPONSE];
|
||||
if(YES == _debug)
|
||||
{
|
||||
NSLog(@"%@: set SENTRESPONSE (-[%@])", self, NSStringFromSelector(_cmd));
|
||||
}
|
||||
}
|
||||
|
||||
- (void)timeoutExceededByHandler:(id)handler
|
||||
{
|
||||
_failed = YES;
|
||||
}
|
||||
/* end of TestWebServerDelegate */
|
||||
|
||||
/* NSURLConnectionDelegate */
|
||||
- (NSURLRequest *)connection:(NSURLConnection *)connection
|
||||
willSendRequest:(NSURLRequest *)request
|
||||
redirectResponse:(NSURLResponse *)redirectResponse
|
||||
{
|
||||
if([redirectResponse isKindOfClass: [NSHTTPURLResponse class]])
|
||||
{
|
||||
if([(NSHTTPURLResponse *)redirectResponse statusCode] == _redirectStatusCode)
|
||||
{
|
||||
[self setFlags: GOTREDIRECT];
|
||||
if(YES == _debug)
|
||||
{
|
||||
NSLog(@"%@: set GOTREDIRECT (-[%@])", self, NSStringFromSelector(_cmd));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
[self setFlags: GOTREDIRECT];
|
||||
if(YES == _debug)
|
||||
{
|
||||
NSLog(@"%@: set GOTREDIRECT (-[%@])", self, NSStringFromSelector(_cmd));
|
||||
}
|
||||
}
|
||||
|
||||
return _redirectRequest;
|
||||
}
|
||||
|
||||
- (void)connection:(NSURLConnection *)connection
|
||||
didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
|
||||
{
|
||||
[self setFlags: GOTUNAUTHORIZED];
|
||||
if(YES == _debug)
|
||||
{
|
||||
NSLog(@"%@: set GOTUNAUTHORIZED (-[%@])", self, NSStringFromSelector(_cmd));
|
||||
}
|
||||
|
||||
if([challenge previousFailureCount] == 0)
|
||||
{
|
||||
NSURLCredential *cred;
|
||||
// TODO: _auxServer?
|
||||
NSString *login = [_server login];
|
||||
NSString *password = [_server password];
|
||||
|
||||
if(nil == login)
|
||||
{
|
||||
if([_extra isKindOfClass: [NSDictionary class]])
|
||||
{
|
||||
login = [(NSDictionary *)_extra objectForKey: @"Login"];
|
||||
}
|
||||
}
|
||||
if(nil == password)
|
||||
{
|
||||
if([_extra isKindOfClass: [NSDictionary class]])
|
||||
{
|
||||
password = [(NSDictionary *)_extra objectForKey: @"Password"];
|
||||
}
|
||||
}
|
||||
cred = [NSURLCredential credentialWithUser: login
|
||||
password: password
|
||||
persistence: NSURLCredentialPersistenceNone];
|
||||
[[challenge sender] useCredential: cred
|
||||
forAuthenticationChallenge: challenge];
|
||||
}
|
||||
else
|
||||
{
|
||||
[[challenge sender] cancelAuthenticationChallenge: challenge];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)connection:(NSURLConnection *)connection
|
||||
didReceiveResponse:(NSURLResponse *)response
|
||||
{
|
||||
if(YES == _debug)
|
||||
{
|
||||
if([response isKindOfClass: [NSHTTPURLResponse class]])
|
||||
{
|
||||
NSLog(@"%@: received response (-[%@]):\nStatus Code: %u",
|
||||
self, NSStringFromSelector(_cmd), [(NSHTTPURLResponse *)response statusCode]);
|
||||
}
|
||||
}
|
||||
|
||||
if([response isKindOfClass: [NSHTTPURLResponse class]])
|
||||
{
|
||||
if([(NSHTTPURLResponse *)response statusCode] == _expectedStatusCode)
|
||||
{
|
||||
[self setFlags: GOTRESPONSE];
|
||||
if(YES == _debug)
|
||||
{
|
||||
NSLog(@"%@: set GOTRESPONSE (-[%@])", self, NSStringFromSelector(_cmd));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
[self setFlags: GOTRESPONSE];
|
||||
if(YES == _debug)
|
||||
{
|
||||
NSLog(@"%@: set GOTRESPONSE (-[%@])", self, NSStringFromSelector(_cmd));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)connection:(NSURLConnection *)connection
|
||||
didReceiveData:(NSData *)data
|
||||
{
|
||||
[_received appendData: data];
|
||||
if(YES == _debug)
|
||||
{
|
||||
NSLog(@"%@: received data '%@' (-[%@])", self, data, NSStringFromSelector(_cmd));
|
||||
}
|
||||
}
|
||||
|
||||
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
|
||||
{
|
||||
if(nil != _expectedContent &&
|
||||
[_received isEqualToData: _expectedContent])
|
||||
{
|
||||
[self setFlags: GOTCONTENT];
|
||||
if(YES == _debug)
|
||||
{
|
||||
NSLog(@"%@: set GOTCONTENT (-[%@])", self, NSStringFromSelector(_cmd));
|
||||
}
|
||||
}
|
||||
|
||||
[self setFlags: GOTFINISH];
|
||||
if(YES == _debug)
|
||||
{
|
||||
NSLog(@"%@: set GOTFINISH (-[%@])", self, NSStringFromSelector(_cmd));
|
||||
}
|
||||
|
||||
_done = YES;
|
||||
}
|
||||
|
||||
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
|
||||
{
|
||||
ASSIGN(_error, error);
|
||||
|
||||
/* if((nil != _expectedContent &&
|
||||
[_received isEqualToData: _expectedContent]) ||
|
||||
(nil == _expectedContent && [_received length] == 0))
|
||||
{
|
||||
[self setFlags: GOTCONTENT];
|
||||
}
|
||||
*/
|
||||
|
||||
[self setFlags: GOTFAIL];
|
||||
if(YES == _debug)
|
||||
{
|
||||
NSLog(@"%@: set GOTFAIL (-[%@])", self, NSStringFromSelector(_cmd));
|
||||
NSLog(@"%@: error %@", self, error);
|
||||
}
|
||||
|
||||
_done = YES;
|
||||
}
|
||||
|
||||
/* end of NSURLConnectionDelegate */
|
||||
|
||||
@end /* NSURLConnectionTest */
|
||||
|
||||
@implementation NSURLConnectionTest (Private)
|
||||
|
||||
- (void)_makeRequest:(id)extra
|
||||
{
|
||||
BOOL isOwnServer = YES;
|
||||
BOOL isOwnAuxServer = NO;
|
||||
NSString *tmp;
|
||||
BOOL isDetached = NO;
|
||||
TestWebServer *instance = nil;
|
||||
TestWebServer *auxInstance = nil;
|
||||
NSString *protocol = nil;
|
||||
NSString *address = nil;
|
||||
NSString *auxPort = nil;
|
||||
NSString *port = nil;
|
||||
// NSString *login = nil;
|
||||
// NSString *password = nil;
|
||||
NSString *path = nil;
|
||||
NSString *redirectPath = nil;
|
||||
NSString *statusCode = nil;
|
||||
NSString *redirectCode;
|
||||
NSString *method = nil;
|
||||
id payload = nil;
|
||||
id content = nil;
|
||||
NSURL *url;
|
||||
NSDictionary *d = nil;
|
||||
|
||||
if([extra isKindOfClass: [NSDictionary class]])
|
||||
{
|
||||
d = extra;
|
||||
|
||||
instance = [d objectForKey: @"Instance"];
|
||||
if(nil != instance)
|
||||
{
|
||||
NSString *value;
|
||||
|
||||
protocol = [instance isSecure] ? @"https" : @"http";
|
||||
if((value = [d objectForKey: @"Protocol"]) == nil ||
|
||||
![[value lowercaseString] isEqualToString: protocol])
|
||||
{
|
||||
d = [d mutableCopy];
|
||||
[(NSMutableDictionary *)d setObject: protocol forKey: @"Protocol"];
|
||||
[d autorelease];
|
||||
}
|
||||
|
||||
address = [instance address];
|
||||
port = [instance port];
|
||||
ASSIGN(_server, instance);
|
||||
[_server setDelegate: self];
|
||||
[_server setDebug: _debug];
|
||||
isOwnServer = NO;
|
||||
}
|
||||
else
|
||||
{
|
||||
protocol = [[d objectForKey: @"Protocol"] lowercaseString];
|
||||
address = [d objectForKey: @"Address"];
|
||||
port = [d objectForKey: @"Port"];
|
||||
}
|
||||
|
||||
auxInstance = [d objectForKey: @"AuxInstance"];
|
||||
if(nil != auxInstance)
|
||||
{
|
||||
auxPort = [auxInstance port];
|
||||
ASSIGN(_auxServer, auxInstance);
|
||||
[_auxServer setDelegate: self];
|
||||
[_auxServer setDebug: _debug];
|
||||
isOwnAuxServer = NO;
|
||||
}
|
||||
|
||||
if(isOwnServer)
|
||||
{
|
||||
if((tmp = [d objectForKey: @"IsDetached"]) != nil)
|
||||
{
|
||||
if([tmp isEqualToString: @"YES"])
|
||||
{
|
||||
isDetached = YES;
|
||||
}
|
||||
else
|
||||
{
|
||||
isDetached = NO;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
isOwnAuxServer = [[d objectForKey: @"IsAuxilliary"] isEqualToString: @"YES"] &&
|
||||
nil == _auxServer;
|
||||
if(isOwnAuxServer)
|
||||
{
|
||||
auxPort = [d objectForKey: @"AuxPort"];
|
||||
}
|
||||
|
||||
path = [d objectForKey: @"Path"];
|
||||
if(isOwnAuxServer || nil != _auxServer)
|
||||
{
|
||||
redirectPath = [d objectForKey: @"RedirectPath"];
|
||||
}
|
||||
statusCode = [d objectForKey: @"StatusCode"];
|
||||
// TODO: conditions?
|
||||
redirectCode = [d objectForKey: @"RedirectCode"];
|
||||
method = [d objectForKey: @"Method"];
|
||||
payload = [d objectForKey: @"Payload"];
|
||||
content = [d objectForKey: @"Content"];
|
||||
} // Is extra NSDictionary?
|
||||
else if([extra isKindOfClass: [NSURLRequest class]])
|
||||
{
|
||||
ASSIGN(_request, extra);
|
||||
}
|
||||
|
||||
if(nil == _request)
|
||||
{
|
||||
if(nil == protocol)
|
||||
{
|
||||
protocol = @"http";
|
||||
}
|
||||
if(nil == address)
|
||||
{
|
||||
address = @"localhost";
|
||||
}
|
||||
if(nil == port)
|
||||
{
|
||||
port = @"54321";
|
||||
}
|
||||
if(nil == auxPort)
|
||||
{
|
||||
auxPort = @"54322";
|
||||
}
|
||||
if(nil == path)
|
||||
{
|
||||
path = @"/";
|
||||
}
|
||||
if((isOwnAuxServer || nil != _auxServer) && nil == redirectPath)
|
||||
{
|
||||
redirectPath = path;
|
||||
}
|
||||
if(nil != redirectPath && nil == redirectCode)
|
||||
{
|
||||
_redirectStatusCode = 301;
|
||||
}
|
||||
if(nil == statusCode)
|
||||
{
|
||||
_expectedStatusCode = 204;
|
||||
}
|
||||
else
|
||||
{
|
||||
_expectedStatusCode = [statusCode intValue];
|
||||
if(_expectedStatusCode == 0)
|
||||
{
|
||||
[NSException raise: NSInvalidArgumentException
|
||||
format: @"Invalid expected 'StatusCode' supplied %@", statusCode];
|
||||
}
|
||||
}
|
||||
if(nil == redirectCode)
|
||||
{
|
||||
_redirectStatusCode = 301;
|
||||
}
|
||||
else
|
||||
{
|
||||
_redirectStatusCode = [redirectCode intValue];
|
||||
if(_redirectStatusCode == 0)
|
||||
{
|
||||
[NSException raise: NSInvalidArgumentException
|
||||
format: @"Invalid expected 'RedirectCode' supplied %@", redirectCode];
|
||||
}
|
||||
}
|
||||
|
||||
if(nil == method)
|
||||
{
|
||||
method = @"GET";
|
||||
}
|
||||
|
||||
if(![path hasPrefix: @"/"])
|
||||
{
|
||||
// path MUST begin with '/'
|
||||
path = [@"/" stringByAppendingString: path];
|
||||
}
|
||||
|
||||
if(![redirectPath hasPrefix: @"/"])
|
||||
{
|
||||
// path MUST begin with '/'
|
||||
redirectPath = [@"/" stringByAppendingString: redirectPath];
|
||||
}
|
||||
|
||||
url = [NSURL URLWithString:
|
||||
[NSString stringWithFormat: @"%@://%@:%@%@", protocol, address, auxPort, redirectPath]];
|
||||
_redirectRequest = [NSMutableURLRequest requestWithURL: url];
|
||||
RETAIN(_redirectRequest);
|
||||
|
||||
url = [NSURL URLWithString:
|
||||
[NSString stringWithFormat: @"%@://%@:%@%@", protocol, address, port, path]];
|
||||
_request = [NSMutableURLRequest requestWithURL: url];
|
||||
RETAIN(_request);
|
||||
|
||||
[(NSMutableURLRequest *)_request setHTTPMethod: method];
|
||||
if(nil != payload)
|
||||
{
|
||||
if([payload isKindOfClass: [NSString class]])
|
||||
{
|
||||
payload = [payload dataUsingEncoding: NSUTF8StringEncoding];
|
||||
}
|
||||
if(![payload isKindOfClass: [NSData class]])
|
||||
{
|
||||
[NSException raise: NSInvalidArgumentException
|
||||
format: @"invalid payload"];
|
||||
}
|
||||
[(NSMutableURLRequest *)_request setHTTPBody: payload];
|
||||
}
|
||||
|
||||
if(nil != content)
|
||||
{
|
||||
if([content isKindOfClass: [NSString class]])
|
||||
{
|
||||
content = [content dataUsingEncoding: NSUTF8StringEncoding];
|
||||
}
|
||||
if(![content isKindOfClass: [NSData class]])
|
||||
{
|
||||
[NSException raise: NSInvalidArgumentException
|
||||
format: @"invalid content"];
|
||||
}
|
||||
ASSIGN(_expectedContent, content);
|
||||
}
|
||||
if(nil != _expectedContent && nil == statusCode)
|
||||
{
|
||||
_expectedStatusCode = 200;
|
||||
}
|
||||
}
|
||||
|
||||
if(isOwnServer)
|
||||
{
|
||||
_server = [[TestWebServer alloc] initWithAddress: address
|
||||
port: port
|
||||
mode: isDetached
|
||||
extra: d];
|
||||
[_server setDebug: _debug];
|
||||
[_server setDelegate: self];
|
||||
[_server start: d];
|
||||
}
|
||||
if(isOwnAuxServer)
|
||||
{
|
||||
_auxServer = [[TestWebServer alloc] initWithAddress: address
|
||||
port: auxPort
|
||||
mode: isDetached
|
||||
extra: d];
|
||||
[_auxServer setDebug: _debug];
|
||||
[_auxServer setDelegate: self];
|
||||
[_auxServer start: d];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_makeReferenceFlags:(id)extra
|
||||
{
|
||||
// trying to guess the execution path
|
||||
// and to infer the reference flag set
|
||||
if(nil != _request)
|
||||
{
|
||||
// default reference set
|
||||
[self setReferenceFlags: SENTREQUEST];
|
||||
[self setReferenceFlags: NOTAUTHORIZED];
|
||||
[self setReferenceFlags: AUTHORIZED];
|
||||
[self setReferenceFlags: GOTUNAUTHORIZED];
|
||||
[self setReferenceFlags: GOTREQUEST];
|
||||
[self setReferenceFlags: SENTRESPONSE];
|
||||
[self setReferenceFlags: GOTRESPONSE];
|
||||
if(nil != _expectedContent)
|
||||
{
|
||||
[self setReferenceFlags: GOTCONTENT];
|
||||
}
|
||||
[self setReferenceFlags: GOTFINISH];
|
||||
}
|
||||
else
|
||||
{
|
||||
[NSException raise: NSInvalidArgumentException
|
||||
format: @"no request"];
|
||||
}
|
||||
|
||||
if([extra isKindOfClass: [NSDictionary class]])
|
||||
{
|
||||
// make correction in the reference flag set
|
||||
NSDictionary *d = extra;
|
||||
if((d = [d objectForKey: @"ReferenceFlags"]) != nil)
|
||||
{
|
||||
NSEnumerator *en = [d keyEnumerator];
|
||||
NSString *key;
|
||||
NSString *value;
|
||||
SEL sel;
|
||||
|
||||
while((key = [en nextObject]) != nil)
|
||||
{
|
||||
value = [d objectForKey: key];
|
||||
NSString *originalKey = nil;
|
||||
NSUInteger flag = NORESULTS;
|
||||
// NSUInteger flag = (NSUInteger)NSMapGet(_flagMap, key);
|
||||
if(NSMapMember(_flagMap, key, (void **)&originalKey, (void **)&flag))
|
||||
{
|
||||
if([value isEqualToString: @"YES"])
|
||||
{
|
||||
sel = @selector(setReferenceFlags:);
|
||||
}
|
||||
else
|
||||
{
|
||||
sel = @selector(unsetReferenceFlags:);
|
||||
}
|
||||
|
||||
[self performSelector: sel withObject: (void *)flag];
|
||||
}
|
||||
else
|
||||
{
|
||||
[NSException raise: NSInternalInconsistencyException
|
||||
format: @"flag codes corrupted (key %@)", key];
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@end /* NSURLConnectionTest (Private) */
|
29
Tests/base/NSURLConnection/Helpers/README
Normal file
29
Tests/base/NSURLConnection/Helpers/README
Normal file
|
@ -0,0 +1,29 @@
|
|||
The directory contains classes useful in testing of web-clients.
|
||||
They are written mainly with the necessity to test the class NSURLConnection
|
||||
in mind.
|
||||
|
||||
TestCase
|
||||
NSURLConnectionTest
|
||||
is a child of TestCase and represents a test of NSURLConnection.
|
||||
It starts a main instance and (if needed) an auxillary instance
|
||||
of TestWebServer (or uses externally supplied ones). Then it makes
|
||||
a custom request to check various functionality of NSURLConnection.
|
||||
|
||||
TestWebServer
|
||||
maintains it's own instance of SimpleWebServer and implements
|
||||
SimpleWebServer delegate's protocol to dispatch any request
|
||||
to a corresponding handler. It can call it's delegate during
|
||||
proceeding of requests.
|
||||
|
||||
RequestHandler
|
||||
TestHandler and it's descendants
|
||||
handle requests (dispatched by TestWebServer) and produces
|
||||
a custom response. It also calls it's delegate (using TestWebServer
|
||||
delegate's protocol) during request handling.
|
||||
|
||||
SimpleWebServer
|
||||
The own implementation of a web server.
|
||||
|
||||
The key protocols important for understanding are TestProgress (TestCase.h)
|
||||
and TestWebServerDelegate (TestWebServer.h). Alternatively the file
|
||||
NSURLConnectionTest.h briefly describes the whole picture.
|
304
Tests/base/NSURLConnection/Helpers/RequestHandler.h
Normal file
304
Tests/base/NSURLConnection/Helpers/RequestHandler.h
Normal file
|
@ -0,0 +1,304 @@
|
|||
/** -*- objc -*-
|
||||
*
|
||||
* Author: Sergei Golovin <Golovin.SV@gmail.com>
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* TODO: there is lack of session handling
|
||||
*/
|
||||
|
||||
#import "TestWebServer.h"
|
||||
|
||||
/**
|
||||
* The protocol RequestHandler describes how a request receiver should interact
|
||||
* with a request handler.
|
||||
*/
|
||||
@protocol RequestHandler
|
||||
|
||||
/**
|
||||
* Does all job needed to prepare for request handling. Returns YES if
|
||||
* the handler still can proceed the supplied request later.
|
||||
* The reason this method was introduced is possible session
|
||||
* handling in the future.
|
||||
*/
|
||||
- (BOOL)prehandleRequest:(GSMimeDocument *)request
|
||||
response:(GSMimeDocument *)response
|
||||
for:(TestWebServer *)server;
|
||||
|
||||
/**
|
||||
* Makes a custom response. Returns YES if the handler has recognized
|
||||
* it is it's responsibility to handle the request.
|
||||
*/
|
||||
- (BOOL) handleRequest:(GSMimeDocument *)request
|
||||
response:(GSMimeDocument *)response
|
||||
for:(TestWebServer *)server;
|
||||
|
||||
/**
|
||||
* Does all job needed to be done after request handling. Returns NO
|
||||
* if the handler has discovered some inconsistency after handling.
|
||||
* The reason this method was introduced is possible session
|
||||
* handling in the future.
|
||||
*/
|
||||
- (BOOL)posthandleRequest:(GSMimeDocument *)request
|
||||
response:(GSMimeDocument *)response
|
||||
for:(TestWebServer *)server;
|
||||
|
||||
@end /* RequestHandler */
|
||||
|
||||
/**
|
||||
* The abstract class TestHandler. Custom handlers must be derived from it.
|
||||
* The class implements the protocol RequestHandler's methods that are called
|
||||
* by an instance of TestWebServer. The protocol's method -[handleRequest:response:for:]
|
||||
* produces a custom response. It also calls it's delegate's callback methods of
|
||||
* the protocol TestWebServerDelegate (if implemented) during proceeding of a request.
|
||||
*
|
||||
* On use of the -[handleRequest:response:for:]
|
||||
* ---------------------------------------------
|
||||
*
|
||||
* The TestHandler's implementation of the -[handleRequest:response:for:] checks
|
||||
* whether the supplied request is authorized. It returns YES if no processing is
|
||||
* required (by a child) so the response must be returned intact. If the method
|
||||
* returns NO then the child MAY process the request on it's own. If the request is
|
||||
* authorized the method also sets the flag _isAuthorized to YES.
|
||||
*
|
||||
* Child's implementation MUST call this super's one as in the following
|
||||
* example code:
|
||||
* ------------------------------------------------------------------------
|
||||
* - (BOOL) handleRequest: (GSMimeDocument*)request
|
||||
* response: (GSMimeDocument*)response
|
||||
* for: (TestWebServer *)server
|
||||
* {
|
||||
* BOOL ret = [super handleRequest: request response: response for: server];
|
||||
* if(NO == ret)
|
||||
* {
|
||||
* // process on it's own with possible checking _isAuthorized if needed
|
||||
* ...
|
||||
* _done = YES;
|
||||
* ret = YES;
|
||||
* }
|
||||
*
|
||||
* return ret;
|
||||
* }
|
||||
* ------------------------------------------------------------------------
|
||||
*
|
||||
* The method recognizes the case when the request's path contains '/withoutauth'.
|
||||
* In this case no authorization is required and the method -[handleRequest:response:for:]
|
||||
* just returns NO and sets the flag _isAuthorized to YES.
|
||||
*
|
||||
*/
|
||||
@interface TestHandler : NSObject <RequestHandler>
|
||||
{
|
||||
/* the debug mode switch */
|
||||
BOOL _debug;
|
||||
/* the handler's delegate... NOT RETAINED */
|
||||
id _delegate;
|
||||
/* the flag signifies the handler has got an authorized request */
|
||||
BOOL _isAuthorized;
|
||||
/* the login for basic authentication */
|
||||
NSString *_login;
|
||||
/* the password for basic authentication */
|
||||
NSString *_password;
|
||||
/* the flag for successful handling...
|
||||
can be used in the -[posthandleRequest:response:for:] */
|
||||
BOOL _done;
|
||||
}
|
||||
|
||||
- (id)init;
|
||||
- (void)dealloc;
|
||||
|
||||
/* getters */
|
||||
/* end of getters */
|
||||
|
||||
/* setters */
|
||||
/**
|
||||
* Switches the debug mode on/off.
|
||||
*/
|
||||
- (void)setDebug:(BOOL)flag;
|
||||
|
||||
/**
|
||||
* Sets the delegate implementing TestWebServerDelegate protocol.
|
||||
*/
|
||||
- (void)setDelegate:(id)delegate;
|
||||
|
||||
/**
|
||||
* Sets the login for requests with authorization.
|
||||
*/
|
||||
- (void)setLogin:(NSString *)login;
|
||||
|
||||
/**
|
||||
* Sets the password for requests with authorization.
|
||||
*/
|
||||
- (void)setPassword:(NSString *)password;
|
||||
|
||||
/* end of setters */
|
||||
|
||||
@end /* TestHandler */
|
||||
|
||||
/* The handler collection */
|
||||
|
||||
/**
|
||||
* The handler returns the status code 200 for any request.
|
||||
* The response's content is 'OK'. The status line is the default one
|
||||
* returned by the TestWebServer. The header 'Content-Type' is set to 'plain/text'.
|
||||
*/
|
||||
@interface Handler200 : TestHandler
|
||||
{
|
||||
}
|
||||
|
||||
@end /* Handler200 */
|
||||
|
||||
/**
|
||||
* The handler returns the status code 204 for any request.
|
||||
* The response's content is empty. The status line is 'HTTP/1.1 204 No Content'.
|
||||
*/
|
||||
@interface Handler204 : TestHandler
|
||||
{
|
||||
}
|
||||
|
||||
@end /* Handler204 */
|
||||
|
||||
/**
|
||||
* The handler returns the status code 301 for any request.
|
||||
* The response's content is an html page like 'Redirect to <a href="URL">'.
|
||||
* The status line is 'HTTP/1.1 301 Moved permanently'. It also sets
|
||||
* the response's header 'Location' to the custom URL stored by the
|
||||
* ivar _URLString. If the -[setURLString:] wasn't called with a proper URL
|
||||
* string before the first request arrives then the handler will raise
|
||||
* NSInternalInconsistencyException.
|
||||
*/
|
||||
@interface Handler301 : TestHandler
|
||||
{
|
||||
NSString *_URLString;
|
||||
}
|
||||
|
||||
- (void)dealloc;
|
||||
|
||||
/**
|
||||
* Sets the URL to which the handler should redirect.
|
||||
*/
|
||||
- (void)setURLString:(NSString *)URLString;
|
||||
|
||||
@end /* Handler301 */
|
||||
|
||||
/**
|
||||
* The handler returns unconditionally the status code 400 for any request.
|
||||
* The response's content is 'You have issued a request with invalid data'.
|
||||
* The status line is 'HTTP/1.1 400 Bad Request'.
|
||||
*/
|
||||
@interface Handler400 : TestHandler
|
||||
{
|
||||
}
|
||||
|
||||
@end /* Handler400 */
|
||||
|
||||
/**
|
||||
* The handler returns unconditionally the status code 401 for any request.
|
||||
* The response's content is 'Invalid login or password'.
|
||||
* The status line is 'HTTP/1.1 401 Unauthorized'. It also sets the response's
|
||||
* header 'WWW-Authenticate' to the value "Basic realm='TestSuit'".
|
||||
*/
|
||||
@interface Handler401 : TestHandler
|
||||
{
|
||||
}
|
||||
|
||||
@end /* Handler401 */
|
||||
|
||||
/**
|
||||
* The handler returns unconditionally the status code 402 for any request.
|
||||
* The response's content is 'Check your balance'.
|
||||
* The status line is 'HTTP/1.1 402 Payment required'.
|
||||
*/
|
||||
@interface Handler402 : TestHandler
|
||||
{
|
||||
}
|
||||
|
||||
@end /* Handler402 */
|
||||
|
||||
/**
|
||||
* The handler returns unconditionally the status code 404 for any request.
|
||||
* The response's content is 'The resource you are trying to access is not found'.
|
||||
* The status line is 'HTTP/1.1 404 Not found'.
|
||||
*/
|
||||
@interface Handler404 : TestHandler
|
||||
{
|
||||
}
|
||||
|
||||
@end /* Handler404 */
|
||||
|
||||
/**
|
||||
* The handler returns unconditionally the status code 405 for any request.
|
||||
* The response's content is 'You can't do that'.
|
||||
* The status line is 'HTTP/1.1 405 Method not allowed'.
|
||||
*/
|
||||
@interface Handler405 : TestHandler
|
||||
{
|
||||
}
|
||||
|
||||
@end /* Handler405 */
|
||||
|
||||
/**
|
||||
* The handler returns unconditionally the status code 409 for any request.
|
||||
* The response's content is 'The resource you are trying to access is busy'.
|
||||
* The status line is 'HTTP/1.1 409 Conflict'.
|
||||
*/
|
||||
@interface Handler409 : TestHandler
|
||||
{
|
||||
}
|
||||
|
||||
@end /* Handler409 */
|
||||
|
||||
/**
|
||||
* The handler returns unconditionally the status code 500 for any request.
|
||||
* The response's content is 'System error'.
|
||||
* The status line is 'HTTP/1.1 500 Internal Server Error'.
|
||||
*/
|
||||
@interface Handler500 : TestHandler
|
||||
{
|
||||
}
|
||||
|
||||
@end /* Handler500 */
|
||||
|
||||
/**
|
||||
* The handler returns unconditionally the status code 505 for any request.
|
||||
* The response's content is 'There is network protocol inconsistency'.
|
||||
* The status line is 'HTTP/1.1 505 Network Protocol Error'.
|
||||
*/
|
||||
@interface Handler505 : TestHandler
|
||||
{
|
||||
}
|
||||
|
||||
@end /* Handler505 */
|
||||
|
||||
/**
|
||||
* The handler returns unconditionally the status code 507 for any request.
|
||||
* The response's content is 'Insufficient storage'.
|
||||
* The status line is 'HTTP/1.1 507 Insufficient storage'.
|
||||
*/
|
||||
@interface Handler507 : TestHandler
|
||||
{
|
||||
}
|
||||
|
||||
@end /* Handler507 */
|
||||
|
||||
/**
|
||||
* The handler returns the index page with a list of possible URLs for any request.
|
||||
* The URLs are constructed from the base URL stored by the ivar _URLString. If
|
||||
* the -[setURLString:] wasn't called with a proper URL string before the first
|
||||
* request arrives then the handler will raise NSInternalInconsistencyException.
|
||||
*/
|
||||
@interface HandlerIndex : TestHandler
|
||||
{
|
||||
NSString *_URLString;
|
||||
}
|
||||
|
||||
- (void)dealloc;
|
||||
|
||||
/**
|
||||
* Sets the URL to which the handler should redirect.
|
||||
*/
|
||||
- (void)setURLString:(NSString *)URLString;
|
||||
|
||||
@end /* HandlerIndex */
|
||||
|
||||
/* The end of the handler collection */
|
601
Tests/base/NSURLConnection/Helpers/RequestHandler.m
Normal file
601
Tests/base/NSURLConnection/Helpers/RequestHandler.m
Normal file
|
@ -0,0 +1,601 @@
|
|||
/** -*- objc -*-
|
||||
*
|
||||
* Author: Sergei Golovin <Golovin.SV@gmail.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#import "RequestHandler.h"
|
||||
#import "TestWebServer.h"
|
||||
|
||||
/**
|
||||
* The abstract class TestHandler. All custom handlers must be derived from it.
|
||||
*/
|
||||
@implementation TestHandler
|
||||
|
||||
- (id)init
|
||||
{
|
||||
if((self = [super init]) != nil)
|
||||
{
|
||||
_debug = NO;
|
||||
_delegate = nil;
|
||||
_isAuthorized = NO;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
_delegate = nil;
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
/* getters */
|
||||
/* end of getters */
|
||||
|
||||
/* setters */
|
||||
- (void)setDebug:(BOOL)flag
|
||||
{
|
||||
_debug = flag;
|
||||
}
|
||||
|
||||
- (void)setDelegate:(id)delegate
|
||||
{
|
||||
_delegate = delegate;
|
||||
}
|
||||
|
||||
- (void)setLogin:(NSString *)login
|
||||
{
|
||||
ASSIGN(_login, login);
|
||||
}
|
||||
|
||||
- (void)setPassword:(NSString *)password
|
||||
{
|
||||
ASSIGN(_password, password);
|
||||
}
|
||||
|
||||
/* end of setters */
|
||||
|
||||
/* RequestHandler */
|
||||
|
||||
- (BOOL)prehandleRequest:(GSMimeDocument *)request
|
||||
response:(GSMimeDocument *)response
|
||||
for:(TestWebServer *)server
|
||||
{
|
||||
_isAuthorized = NO; // TODO: move to something like -[reset]
|
||||
_done = NO;
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL) handleRequest: (GSMimeDocument*)request
|
||||
response: (GSMimeDocument*)response
|
||||
for: (TestWebServer*)server
|
||||
{
|
||||
NSString *auth;
|
||||
NSString *path;
|
||||
BOOL ret = NO;
|
||||
|
||||
|
||||
|
||||
if(YES == _debug)
|
||||
{
|
||||
NSLog(@"%@: BEGIN\n%@", self, request);
|
||||
}
|
||||
|
||||
if(nil != _delegate && [_delegate respondsToSelector: @selector(handler:gotRequest:with:)])
|
||||
{
|
||||
[_delegate handler: self gotRequest: request with: server];
|
||||
}
|
||||
|
||||
// analyze what the client wants
|
||||
path = [[request headerNamed:@"x-http-path"] value];
|
||||
|
||||
if([path rangeOfString: @"withoutauth"].location == NSNotFound)
|
||||
{
|
||||
auth = [[request headerNamed: @"Authorization"] value];
|
||||
if(!auth) // not authorized
|
||||
{
|
||||
[response setHeader: @"http" value: @"HTTP/1.1 401 Unauthorized" parameters: nil];
|
||||
[response setHeader: @"WWW-Authenticate" value: @"Basic realm='TestSuit'" parameters: nil];
|
||||
[response setContent: @"Please give login and password"];
|
||||
|
||||
if(nil != _delegate && [_delegate respondsToSelector: @selector(handler:willSendUnauthorized:with:)])
|
||||
{
|
||||
[_delegate handler: self willSendUnauthorized: response with: server];
|
||||
}
|
||||
|
||||
if(YES == _debug)
|
||||
{
|
||||
NSLog(@"%@: about to send Unauthorized\n%@", self, response);
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
else // the auth header is set
|
||||
{
|
||||
NSArray *credentials;
|
||||
|
||||
credentials = [auth componentsSeparatedByString: @" "];
|
||||
if([[credentials objectAtIndex:0] isEqualToString: @"Basic"])
|
||||
{
|
||||
credentials = [[GSMimeDocument decodeBase64String: [credentials objectAtIndex:1]]
|
||||
componentsSeparatedByString: @":"];
|
||||
if([[credentials objectAtIndex:0] isEqualToString: _login] &&
|
||||
[[credentials objectAtIndex:1] isEqualToString: _password])
|
||||
{
|
||||
if(YES == _debug)
|
||||
{
|
||||
NSLog(@"%@: got valid credentials", self);
|
||||
}
|
||||
|
||||
if(nil != _delegate && [_delegate respondsToSelector: @selector(handler:gotAuthorized:with:)])
|
||||
{
|
||||
[_delegate handler: self gotAuthorized: request with: server];
|
||||
}
|
||||
|
||||
// sets the flag
|
||||
_isAuthorized = YES;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
[response setHeader: @"http" value: @"HTTP/1.1 401 Unauthorized" parameters: nil];
|
||||
[response setHeader: @"WWW-Authenticate" value: @"Basic realm='TestSuit'" parameters: nil];
|
||||
[response setContent: @"Try another login or password"];
|
||||
|
||||
if(nil != _delegate && [_delegate respondsToSelector: @selector(handler:willSendUnauthorized:with:)])
|
||||
{
|
||||
[_delegate handler: self willSendUnauthorized: response with: server];
|
||||
}
|
||||
|
||||
if(YES == _debug)
|
||||
{
|
||||
NSLog(@"%@: about to send Unauthorized\n%@", self, response);
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
} // ! withoutauth
|
||||
else
|
||||
{
|
||||
// just sets the flag
|
||||
_isAuthorized = YES;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
- (BOOL)posthandleRequest:(GSMimeDocument *)request
|
||||
response:(GSMimeDocument *)response
|
||||
for:(TestWebServer *)server
|
||||
{
|
||||
BOOL ret = NO;
|
||||
|
||||
if(YES == _done &&
|
||||
nil != _delegate &&
|
||||
[_delegate respondsToSelector: @selector(handler:willSend:with:)])
|
||||
{
|
||||
[_delegate handler: self willSend: response with: server];
|
||||
|
||||
ret = YES;
|
||||
}
|
||||
|
||||
// TODO: may be to move at another place?
|
||||
if(YES == _debug)
|
||||
{
|
||||
NSLog(@"%@: about to send\n%@", self, response);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* end of RequestHandler */
|
||||
|
||||
|
||||
@end /* TestHandler */
|
||||
|
||||
@implementation Handler200
|
||||
|
||||
- (BOOL) handleRequest: (GSMimeDocument*)request
|
||||
response: (GSMimeDocument*)response
|
||||
for: (TestWebServer*)server
|
||||
{
|
||||
BOOL ret = [super handleRequest: request response: response for: server];
|
||||
if(NO == ret)
|
||||
{
|
||||
BOOL isToSetResponse = (nil == [response content]) || ([[response content] length] == 0);
|
||||
if(isToSetResponse)
|
||||
{
|
||||
[response setContent: @"OK"];
|
||||
}
|
||||
[response setHeader: @"HTTP" value: @"HTTP/1.1 200 OK" parameters:nil];
|
||||
[response setHeader: @"content-type" value: @"plain/text" parameters: nil];
|
||||
_done = YES;
|
||||
ret = YES;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@end /* Handler200 */
|
||||
|
||||
@implementation Handler204
|
||||
|
||||
- (BOOL) handleRequest: (GSMimeDocument*)request
|
||||
response: (GSMimeDocument*)response
|
||||
for: (TestWebServer*)server
|
||||
{
|
||||
BOOL ret = [super handleRequest: request response: response for: server];
|
||||
if(NO == ret)
|
||||
{
|
||||
[response setContent: @""];
|
||||
[response setHeader: @"HTTP" value: @"HTTP/1.1 204 No Content" parameters:nil];
|
||||
|
||||
_done = YES;
|
||||
ret = YES;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@end /* Handler204 */
|
||||
|
||||
@implementation Handler301
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
DESTROY(_URLString);
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (void)setURLString:(NSString *)URLString
|
||||
{
|
||||
ASSIGN(_URLString, URLString);
|
||||
}
|
||||
|
||||
- (BOOL) handleRequest: (GSMimeDocument*)request
|
||||
response: (GSMimeDocument*)response
|
||||
for: (TestWebServer*)server
|
||||
{
|
||||
BOOL ret = [super handleRequest: request response: response for: server];
|
||||
if(NO == ret)
|
||||
{
|
||||
if(nil != _URLString)
|
||||
{
|
||||
BOOL isToSetResponse = (nil == [response content]) || ([[response content] length] == 0);
|
||||
[response setHeader: @"HTTP" value: @"HTTP/1.1 301 Moved Permanently" parameters: nil];
|
||||
[response setHeader: @"Location" value: _URLString parameters: nil];
|
||||
if(isToSetResponse)
|
||||
{
|
||||
NSString *method = [[request headerNamed: @"x-http-method"] value];
|
||||
if(![method isEqualToString: @"HEAD"])
|
||||
{
|
||||
NSString *note = [NSString stringWithFormat: @"<html><head></head><body>Redirect to <a href='%@'/></body></html>",
|
||||
_URLString];
|
||||
[response setContent: note];
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
[NSException raise: NSInternalInconsistencyException
|
||||
format: @"%@: URL isn't set", self];
|
||||
}
|
||||
|
||||
_done = YES;
|
||||
ret = YES;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@end /* Handler301 */
|
||||
|
||||
@implementation Handler400
|
||||
|
||||
- (BOOL) handleRequest: (GSMimeDocument*)request
|
||||
response: (GSMimeDocument*)response
|
||||
for: (TestWebServer*)server
|
||||
{
|
||||
BOOL ret = [super handleRequest: request response: response for: server];
|
||||
if(NO == ret)
|
||||
{
|
||||
BOOL isToSetResponse = (nil == [response content]) || ([[response content] length] == 0);
|
||||
|
||||
if(isToSetResponse)
|
||||
{
|
||||
[response setContent: @"You have issued a request with invalid data"];
|
||||
}
|
||||
[response setHeader:@"HTTP" value:@"HTTP/1.1 400 Bad Request" parameters:nil];
|
||||
|
||||
_done = YES;
|
||||
ret = YES;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@end /* Handler400 */
|
||||
|
||||
@implementation Handler401
|
||||
|
||||
- (BOOL) handleRequest: (GSMimeDocument*)request
|
||||
response: (GSMimeDocument*)response
|
||||
for: (TestWebServer*)server
|
||||
{
|
||||
BOOL ret = [super handleRequest: request response: response for: server];
|
||||
if(NO == ret)
|
||||
{
|
||||
BOOL isToSetResponse = (nil == [response content]) || ([[response content] length] == 0);
|
||||
|
||||
[response setHeader:@"HTTP" value: @"HTTP/1.1 401 Unauthorized" parameters:nil];
|
||||
[response setHeader:@"WWW-Authenticate" value: @"Basic realm='TestSuit'" parameters:nil];
|
||||
if(isToSetResponse)
|
||||
{
|
||||
[response setContent: @"Invalid login or password"];
|
||||
}
|
||||
|
||||
_done = YES;
|
||||
ret = YES;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@end /* Handler401 */
|
||||
|
||||
@implementation Handler402
|
||||
|
||||
- (BOOL) handleRequest: (GSMimeDocument*)request
|
||||
response: (GSMimeDocument*)response
|
||||
for: (TestWebServer*)server
|
||||
{
|
||||
BOOL ret = [super handleRequest: request response: response for: server];
|
||||
if(NO == ret)
|
||||
{
|
||||
BOOL isToSetResponse = (nil == [response content]) || ([[response content] length] == 0);
|
||||
|
||||
[response setHeader:@"HTTP" value:@"HTTP/1.1 402 Payment required" parameters:nil];
|
||||
if(isToSetResponse)
|
||||
{
|
||||
[response setContent: @"Check your balance"];
|
||||
}
|
||||
|
||||
_done = YES;
|
||||
ret = YES;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@end /* Handler402 */
|
||||
|
||||
@implementation Handler404
|
||||
|
||||
- (BOOL) handleRequest: (GSMimeDocument*)request
|
||||
response: (GSMimeDocument*)response
|
||||
for: (TestWebServer*)server
|
||||
{
|
||||
BOOL ret = [super handleRequest: request response: response for: server];
|
||||
if(NO == ret)
|
||||
{
|
||||
BOOL isToSetResponse = (nil == [response content]) || ([[response content] length] == 0);
|
||||
|
||||
[response setHeader:@"HTTP" value:@"HTTP/1.1 404 Not found" parameters:nil];
|
||||
if(isToSetResponse)
|
||||
{
|
||||
[response setContent: @"The resource you are trying to access is not found"];
|
||||
}
|
||||
|
||||
_done = YES;
|
||||
ret = YES;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@end /* Handler404 */
|
||||
|
||||
@implementation Handler405
|
||||
|
||||
- (BOOL) handleRequest: (GSMimeDocument*)request
|
||||
response: (GSMimeDocument*)response
|
||||
for: (TestWebServer*)server
|
||||
{
|
||||
BOOL ret = [super handleRequest: request response: response for: server];
|
||||
if(NO == ret)
|
||||
{
|
||||
BOOL isToSetResponse = (nil == [response content]) || ([[response content] length] == 0);
|
||||
|
||||
[response setHeader:@"HTTP" value:@"HTTP/1.1 405 Method not allowed" parameters:nil];
|
||||
if(isToSetResponse)
|
||||
{
|
||||
[response setContent: @"You can't do that"];
|
||||
}
|
||||
|
||||
_done = YES;
|
||||
ret = YES;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@end /* Handler405 */
|
||||
|
||||
@implementation Handler409
|
||||
|
||||
- (BOOL) handleRequest: (GSMimeDocument*)request
|
||||
response: (GSMimeDocument*)response
|
||||
for: (TestWebServer*)server
|
||||
{
|
||||
BOOL ret = [super handleRequest: request response: response for: server];
|
||||
if(NO == ret)
|
||||
{
|
||||
BOOL isToSetResponse = (nil == [response content]) || ([[response content] length] == 0);
|
||||
|
||||
[response setHeader:@"HTTP" value:@"HTTP/1.1 409 Conflict" parameters:nil];
|
||||
if(isToSetResponse)
|
||||
{
|
||||
[response setContent: @"The resource you are trying to access is busy"];
|
||||
}
|
||||
|
||||
_done = YES;
|
||||
ret = YES;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@end /* Handler409 */
|
||||
|
||||
@implementation Handler500
|
||||
|
||||
- (BOOL) handleRequest: (GSMimeDocument*)request
|
||||
response: (GSMimeDocument*)response
|
||||
for: (TestWebServer*)server
|
||||
{
|
||||
BOOL ret = [super handleRequest: request response: response for: server];
|
||||
if(NO == ret)
|
||||
{
|
||||
BOOL isToSetResponse = (nil == [response content]) || ([[response content] length] == 0);
|
||||
|
||||
[response setHeader:@"HTTP" value:@"HTTP/1.1 500 Internal Server Error" parameters:nil];
|
||||
if(isToSetResponse)
|
||||
{
|
||||
[response setContent: @"System error"];
|
||||
}
|
||||
|
||||
_done = YES;
|
||||
ret = YES;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@end /* Handler500 */
|
||||
|
||||
@implementation Handler505
|
||||
|
||||
- (BOOL) handleRequest: (GSMimeDocument*)request
|
||||
response: (GSMimeDocument*)response
|
||||
for: (TestWebServer*)server
|
||||
{
|
||||
BOOL ret = [super handleRequest: request response: response for: server];
|
||||
if(NO == ret)
|
||||
{
|
||||
BOOL isToSetResponse = (nil == [response content]) || ([[response content] length] == 0);
|
||||
|
||||
[response setHeader:@"HTTP" value:@"HTTP/1.1 505 Network Protocol Error" parameters:nil];
|
||||
if(isToSetResponse)
|
||||
{
|
||||
[response setContent: @"There is network protocol inconsistency"];
|
||||
}
|
||||
|
||||
_done = YES;
|
||||
ret = YES;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@end /* Handler505 */
|
||||
|
||||
@implementation Handler507
|
||||
|
||||
- (BOOL) handleRequest: (GSMimeDocument*)request
|
||||
response: (GSMimeDocument*)response
|
||||
for: (TestWebServer*)server
|
||||
{
|
||||
BOOL ret = [super handleRequest: request response: response for: server];
|
||||
if(NO == ret)
|
||||
{
|
||||
BOOL isToSetResponse = (nil == [response content]) || ([[response content] length] == 0);
|
||||
|
||||
[response setHeader:@"HTTP" value: @"HTTP/1.1 507 Insufficient storage" parameters:nil];
|
||||
if(isToSetResponse)
|
||||
{
|
||||
[response setContent: @"Insufficient storage"];
|
||||
}
|
||||
|
||||
_done = YES;
|
||||
ret = YES;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@end /* Handler507 */
|
||||
|
||||
@implementation HandlerIndex
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
DESTROY(_URLString);
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (void)setURLString:(NSString *)URLString
|
||||
{
|
||||
ASSIGN(_URLString, URLString);
|
||||
}
|
||||
|
||||
- (BOOL) handleRequest: (GSMimeDocument*)request
|
||||
response: (GSMimeDocument*)response
|
||||
for: (TestWebServer*)server
|
||||
{
|
||||
BOOL ret = [super handleRequest: request response: response for: server];
|
||||
if(NO == ret)
|
||||
{
|
||||
if(nil != _URLString)
|
||||
{
|
||||
BOOL isToSetResponse = (nil == [response content]) || ([[response content] length] == 0);
|
||||
if(isToSetResponse)
|
||||
{
|
||||
NSString *pattern = @"<html xmlns='http://www.w3.org/1999/xhtml' xml:lang='en' lang='en'><head></head><body>\
|
||||
<a href='%@%@'>This page</a><br> \
|
||||
<a href='%@%@'>Without authentication</a><br> \
|
||||
<a href='%@%@'>301</a><br> \
|
||||
<a href='%@%@'>400</a><br> \
|
||||
<a href='%@%@'>401</a><br> \
|
||||
<a href='%@%@'>402</a><br> \
|
||||
<a href='%@%@'>404</a><br> \
|
||||
<a href='%@%@'>405</a><br> \
|
||||
<a href='%@%@'>409</a><br> \
|
||||
<a href='%@%@'>500</a><br> \
|
||||
<a href='%@%@'>505</a><br> \
|
||||
<a href='%@%@'>507</a><br> \
|
||||
</body></html>";
|
||||
NSString *index = [NSString stringWithFormat: pattern,
|
||||
_URLString, @"index",
|
||||
_URLString, @"withoutauth",
|
||||
_URLString, @"301",
|
||||
_URLString, @"400",
|
||||
_URLString, @"401",
|
||||
_URLString, @"402",
|
||||
_URLString, @"404",
|
||||
_URLString, @"405",
|
||||
_URLString, @"409",
|
||||
_URLString, @"500",
|
||||
_URLString, @"505",
|
||||
_URLString, @"507"];
|
||||
[response setContent: index];
|
||||
[response setHeader: @"content-type" value: @"text/html" parameters: nil];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
[NSException raise: NSInternalInconsistencyException
|
||||
format: @"%@: URL isn't set", self];
|
||||
}
|
||||
|
||||
_done = YES;
|
||||
ret = YES;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@end /* HandlerIndex */
|
||||
|
131
Tests/base/NSURLConnection/Helpers/SimpleWebServer.h
Normal file
131
Tests/base/NSURLConnection/Helpers/SimpleWebServer.h
Normal file
|
@ -0,0 +1,131 @@
|
|||
/** -*- objc -*-
|
||||
*
|
||||
* Author: Sergei Golovin <Golovin.SV@gmail.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <GNUstepBase/GSMime.h>
|
||||
|
||||
/**
|
||||
* Implements a simple web server with delegate interaction which mimic of
|
||||
* the WebServer's delegate protocol.
|
||||
*
|
||||
* The SimpleWebServer class currently has many limitations (deficiencies).
|
||||
* The following is a list of most important ones:
|
||||
* - supports only one connection simultaneously.
|
||||
* - doesn't support any transfer-content-encoding (more precisely it uses
|
||||
* only 'identity' transfer-content-encoding that is without any modification
|
||||
* of the content/message);
|
||||
* - the class uses UTF8 by default. It expects a request and produces responses
|
||||
* in that encoding;
|
||||
* - it expects an explicit request for closing of the connection (that is
|
||||
* the request's header 'Connection' must be 'close') or implicitly does it
|
||||
* if no 'Connection' has been supplied;
|
||||
* - doesn't support pipelining of requests;
|
||||
*
|
||||
* Use the -[setDebug: YES] to raise verbosity.
|
||||
*/
|
||||
@interface SimpleWebServer : NSObject
|
||||
{
|
||||
/* holds the GSServerStream accepting incoming connections */
|
||||
GSServerStream *_serverStream;
|
||||
/* the delegate ... NOT RETAINED...
|
||||
* see below the protocol SimpleWebServerDelegate */
|
||||
id _delegate;
|
||||
/* the debug mode */
|
||||
BOOL _debug;
|
||||
/* the address to bind with */
|
||||
NSString *_address;
|
||||
/* the port to listen to */
|
||||
NSString *_port;
|
||||
/* SSL configuration and options */
|
||||
NSDictionary *_secure;
|
||||
|
||||
/* The following web-server code is derived/stolen from NSURL/Helpers/capture.m */
|
||||
|
||||
/* the stream to send */
|
||||
NSOutputStream *_op;
|
||||
/* the stream to receive */
|
||||
NSInputStream *_ip;
|
||||
/* the collector of received bytes from a client */
|
||||
NSMutableData *_capture;
|
||||
/* the number of sent bytes to a client */
|
||||
unsigned _written;
|
||||
/* the flag indicating the instance is collecting bytes from a client */
|
||||
BOOL _readable;
|
||||
/* the flag indicating the instance is sending bytes to a client */
|
||||
BOOL _writable;
|
||||
/* whether to use a secure TLS/SSL connection */
|
||||
BOOL _isSecure;
|
||||
/* the request is read */
|
||||
BOOL _doRespond;
|
||||
/* the response is written */
|
||||
BOOL _done;
|
||||
/* end of the stolen */
|
||||
|
||||
/* wether the output stream is ready to write */
|
||||
BOOL _canRespond;
|
||||
|
||||
/* holds the current request */
|
||||
GSMimeDocument *_request;
|
||||
/* holds the current response */
|
||||
GSMimeDocument *_response;
|
||||
}
|
||||
- (void)dealloc;
|
||||
|
||||
/* getters */
|
||||
/**
|
||||
* Returns the string of the port number if the instance is accepting
|
||||
* connections (is started). Otherwise returns nil.
|
||||
*/
|
||||
- (NSString *)port;
|
||||
/* end of getters */
|
||||
|
||||
/* setters */
|
||||
/**
|
||||
* Starts the simple web server listening on the supplied address and port.
|
||||
* The dictionary 'dict' is supplied with additional configuration parameters.
|
||||
* connections. The dictionary's keys are:
|
||||
* CertificateFile
|
||||
* the path to a certificate (if the web server should wait for HTTPS)
|
||||
* KeyFile
|
||||
* the path to a key (if the web server should wait for HTTPS)
|
||||
*/
|
||||
- (BOOL)setAddress:(NSString *)address
|
||||
port:(NSString *)port
|
||||
secure:(NSDictionary *)dict;
|
||||
/**
|
||||
* Sets the debug mode.
|
||||
*/
|
||||
- (void)setDebug:(BOOL)flag;
|
||||
|
||||
/**
|
||||
* Sets the delegate responding to the selector -[processRequest:response:].
|
||||
*/
|
||||
- (void)setDelegate:(id)delegate;
|
||||
/* end of setters */
|
||||
|
||||
/**
|
||||
* Commands the web server to stop listening.
|
||||
*/
|
||||
- (void)stop;
|
||||
|
||||
/* The method is derived/stolen from NSURL/Helpers/capture.m */
|
||||
- (void) stream: (NSStream *)theStream handleEvent: (NSStreamEvent)streamEvent;
|
||||
|
||||
@end /* SimpleWebServer */
|
||||
|
||||
@protocol SimpleWebServerDelegate
|
||||
/**
|
||||
* An implementor gets the supplied request for processing and
|
||||
* modifies the supplied response which the supplied SimpleWebServer
|
||||
* server should send back to it's peer if the return value is set
|
||||
* to YES. Otherwise SimpleWebServer sends the predetermined response
|
||||
* (TODO).
|
||||
*/
|
||||
- (BOOL)processRequest:(GSMimeDocument *)request
|
||||
response:(GSMimeDocument *)response
|
||||
for:(SimpleWebServer *)server;
|
||||
|
||||
@end /* SimpleWebServerDelegate */
|
606
Tests/base/NSURLConnection/Helpers/SimpleWebServer.m
Normal file
606
Tests/base/NSURLConnection/Helpers/SimpleWebServer.m
Normal file
|
@ -0,0 +1,606 @@
|
|||
/*
|
||||
* Author: Sergei Golovin <Golovin.SV@gmail.com>
|
||||
*/
|
||||
|
||||
#import "SimpleWebServer.h"
|
||||
|
||||
/* the time step for the runloop */
|
||||
#define TIMING 0.1
|
||||
|
||||
@interface SimpleWebServer (Private)
|
||||
/**
|
||||
* Starts a listening (server) stream.
|
||||
*/
|
||||
- (void)_openServerStream;
|
||||
|
||||
/**
|
||||
* Stops the listening (server) stream.
|
||||
*/
|
||||
- (void)_closeServerStream;
|
||||
|
||||
/**
|
||||
* Reset to prepare for the next request-response cycle.
|
||||
*/
|
||||
- (void)_resetCycle;
|
||||
|
||||
/**
|
||||
* Opens the input _ip and output _op streams, schedules them
|
||||
* on the current runloop. Requires the _ip and _op are not nil.
|
||||
*/
|
||||
- (void)_openIOStreams;
|
||||
|
||||
/**
|
||||
* Closes the input _ip and output _op streams, unschedules them
|
||||
* on the current runloop and clears that ivars.
|
||||
*/
|
||||
- (void)_closeIOStreams;
|
||||
|
||||
/**
|
||||
* Tries to recognise if the request's bytes have been read (HTTP message's
|
||||
* headers and body) and if so then it proceeds the request and produces
|
||||
* a response. If the response is ready it returns YES.
|
||||
*/
|
||||
- (BOOL)_tryCaptured;
|
||||
|
||||
@end /* SimpleWebServer (Private) */
|
||||
|
||||
@implementation SimpleWebServer
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
_delegate = nil;
|
||||
DESTROY(_capture);
|
||||
DESTROY(_request);
|
||||
DESTROY(_response);
|
||||
[self _closeIOStreams];
|
||||
[self _closeServerStream];
|
||||
DESTROY(_address);
|
||||
DESTROY(_port);
|
||||
DESTROY(_secure);
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
if((self = [super init]) != nil)
|
||||
{
|
||||
_debug = NO;
|
||||
_delegate = nil;
|
||||
_doRespond = NO;
|
||||
_done = NO;
|
||||
_canRespond = NO;
|
||||
_ip = nil;
|
||||
_op = nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/* getters */
|
||||
- (NSString *)port
|
||||
{
|
||||
NSStream *s = _serverStream;
|
||||
|
||||
if(nil == s) s = _ip;
|
||||
if(nil == s) s = _op;
|
||||
if(nil == s) return nil;
|
||||
|
||||
return [s streamStatus] == NSStreamStatusOpen ? _port : nil;
|
||||
}
|
||||
/* end of getters */
|
||||
|
||||
/* setters */
|
||||
- (BOOL)setAddress:(NSString *)address
|
||||
port:(NSString *)port
|
||||
secure:(NSDictionary *)dict
|
||||
{
|
||||
BOOL ret = NO;
|
||||
|
||||
if(nil == _serverStream)
|
||||
{
|
||||
ASSIGN(_address, address);
|
||||
ASSIGN(_port, port);
|
||||
ASSIGN(_secure, dict);
|
||||
[self _openServerStream];
|
||||
if(nil != _serverStream)
|
||||
{
|
||||
ret = YES;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NSLog(@"%@: already started '%@' on '%@'", self, _serverStream, [NSThread currentThread]);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
- (void)setDebug:(BOOL)flag
|
||||
{
|
||||
_debug = flag;
|
||||
}
|
||||
|
||||
- (void)setDelegate:(id)delegate
|
||||
{
|
||||
_delegate = delegate;
|
||||
}
|
||||
|
||||
/* end of setters */
|
||||
|
||||
- (void)stop
|
||||
{
|
||||
NSRunLoop *rl = [NSRunLoop currentRunLoop];;
|
||||
|
||||
if(nil != _serverStream)
|
||||
{
|
||||
[self _closeServerStream];
|
||||
DESTROY(_address);
|
||||
DESTROY(_port);
|
||||
DESTROY(_secure);
|
||||
}
|
||||
|
||||
[self _closeIOStreams];
|
||||
|
||||
// give a time slice to free all resources
|
||||
[rl runUntilDate: [NSDate dateWithTimeIntervalSinceNow: TIMING]];
|
||||
}
|
||||
|
||||
/* GSStream's delegate */
|
||||
|
||||
/* The method is derived/stolen from NSURL/Helpers/capture.m */
|
||||
- (void) stream: (NSStream *)theStream handleEvent: (NSStreamEvent)streamEvent
|
||||
{
|
||||
NSRunLoop *rl = [NSRunLoop currentRunLoop];
|
||||
|
||||
// NSLog(@"Event %p %d", theStream, streamEvent);
|
||||
|
||||
switch (streamEvent)
|
||||
{
|
||||
case NSStreamEventHasBytesAvailable:
|
||||
{
|
||||
if (_ip == nil)
|
||||
{
|
||||
[self _resetCycle];
|
||||
[(GSServerStream*)_serverStream acceptWithInputStream: &_ip
|
||||
outputStream: &_op];
|
||||
if (_ip) // it is ok to accept nothing
|
||||
{
|
||||
RETAIN(_ip);
|
||||
RETAIN(_op);
|
||||
[self _openIOStreams];
|
||||
[self _closeServerStream];
|
||||
}
|
||||
}
|
||||
if (theStream == _ip)
|
||||
{
|
||||
_readable = YES;
|
||||
while (_readable == YES)
|
||||
{
|
||||
unsigned char buffer[BUFSIZ];
|
||||
int readSize;
|
||||
|
||||
readSize = [_ip read: buffer maxLength: sizeof(buffer)];
|
||||
if (readSize <= 0)
|
||||
{
|
||||
_readable = NO;
|
||||
}
|
||||
else
|
||||
{
|
||||
[_capture appendBytes: buffer length: readSize];
|
||||
}
|
||||
}
|
||||
|
||||
_doRespond = [self _tryCaptured];
|
||||
if(_doRespond)
|
||||
{
|
||||
// reset the output stream to trigger polling
|
||||
[_op write: NULL maxLength: 0];
|
||||
if(YES == _debug)
|
||||
{
|
||||
NSLog(@"%@: about to send response\n%@", self, _response);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NSStreamEventHasSpaceAvailable:
|
||||
{
|
||||
if(_doRespond && _canRespond)
|
||||
{
|
||||
NSMutableData *data;
|
||||
NSString *status;
|
||||
NSData *statusData;
|
||||
char *crlf = "\r\n";
|
||||
id content;
|
||||
NSData *contentData;
|
||||
NSUInteger cLength = 0; // content-length
|
||||
NSString *connection;
|
||||
BOOL close = YES;
|
||||
|
||||
NSAssert(theStream == _op, @"Wrong stream for writing");
|
||||
_writable = YES;
|
||||
|
||||
// adding the 'Connection' to the response
|
||||
connection = [[_request headerNamed: @"Connection"] value];
|
||||
if(nil == connection)
|
||||
{
|
||||
connection = [[_request headerNamed: @"connection"] value];
|
||||
}
|
||||
// if the client didn't supply the header 'Connection' or explicitly stated
|
||||
// to close the current connection
|
||||
close = (nil == connection || [[connection lowercaseString] isEqualToString: @"close"]);
|
||||
|
||||
// adding the 'Content-Length' to the response
|
||||
content = [_response content];
|
||||
if([content isKindOfClass: [NSString class]])
|
||||
{
|
||||
contentData = [(NSString *)content dataUsingEncoding: NSUTF8StringEncoding];
|
||||
}
|
||||
else if([content isKindOfClass: [NSData class]])
|
||||
{
|
||||
contentData = (NSData *)content;
|
||||
}
|
||||
else
|
||||
{
|
||||
// yet unsupported
|
||||
}
|
||||
if(nil != content)
|
||||
{
|
||||
cLength = [contentData length];
|
||||
if(cLength > 0)
|
||||
{
|
||||
[_response setHeader: @"Content-Length"
|
||||
value: [NSString stringWithFormat: @"%u", cLength]
|
||||
parameters: nil];
|
||||
}
|
||||
}
|
||||
if(cLength == 0)
|
||||
{
|
||||
[_response setHeader: @"Content-Length"
|
||||
value: @"0"
|
||||
parameters: nil];
|
||||
}
|
||||
|
||||
// adding the status line
|
||||
status = [[_response headerNamed: @"http"] value];
|
||||
if(nil == status)
|
||||
{
|
||||
status = [[_response headerNamed: @"HTTP"] value];
|
||||
}
|
||||
if(nil == status)
|
||||
{
|
||||
status = [[_response headerNamed: @"Http"] value];
|
||||
}
|
||||
statusData = [status dataUsingEncoding: NSUTF8StringEncoding];
|
||||
data = [NSMutableData dataWithData: statusData];
|
||||
[_response deleteHeaderNamed: @"http"];
|
||||
[data appendBytes: crlf length: 2];
|
||||
|
||||
// actual sending
|
||||
[data appendData: [_response rawMimeData]];
|
||||
while (_writable == YES && _written < [data length])
|
||||
{
|
||||
int result = [_op write: [data bytes] + _written
|
||||
maxLength: [data length] - _written];
|
||||
|
||||
if (result <= 0)
|
||||
{
|
||||
_writable = NO;
|
||||
}
|
||||
else
|
||||
{
|
||||
_written += result;
|
||||
}
|
||||
}
|
||||
if (_written == [data length])
|
||||
{
|
||||
if(close)
|
||||
{
|
||||
// if the client didn't supply the header 'Connection' or explicitly stated
|
||||
// to close the current connection
|
||||
[self _closeIOStreams];
|
||||
[self _openServerStream];
|
||||
}
|
||||
// ready for another request-response cycle
|
||||
[self _resetCycle];
|
||||
}
|
||||
}
|
||||
|
||||
_canRespond = YES;
|
||||
|
||||
break;
|
||||
}
|
||||
case NSStreamEventEndEncountered:
|
||||
{
|
||||
if(theStream == _ip || theStream == _op)
|
||||
{
|
||||
[self _closeIOStreams];
|
||||
[self _resetCycle];
|
||||
[self _openServerStream];
|
||||
}
|
||||
else
|
||||
{
|
||||
[theStream close];
|
||||
[theStream removeFromRunLoop: rl forMode: NSDefaultRunLoopMode];
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case NSStreamEventErrorOccurred:
|
||||
{
|
||||
int code = [[theStream streamError] code];
|
||||
|
||||
if(theStream == _ip || theStream == _op)
|
||||
{
|
||||
[self _closeIOStreams];
|
||||
[self _resetCycle];
|
||||
[self _openServerStream];
|
||||
}
|
||||
else
|
||||
{
|
||||
[theStream close];
|
||||
[theStream removeFromRunLoop: rl forMode: NSDefaultRunLoopMode];
|
||||
}
|
||||
|
||||
NSAssert1(1, @"Error! code is %d", code);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* end of GSStream's delegate */
|
||||
|
||||
@end /* SimpleWebServer */
|
||||
|
||||
@implementation SimpleWebServer (Private)
|
||||
|
||||
- (void)_openServerStream
|
||||
{
|
||||
NSString *certFile = nil;
|
||||
NSString *keyFile = nil;
|
||||
NSRunLoop *rl;
|
||||
|
||||
if(nil == _serverStream)
|
||||
{
|
||||
rl = [NSRunLoop currentRunLoop];
|
||||
|
||||
_serverStream = [GSServerStream serverStreamToAddr: _address port: [_port intValue]];
|
||||
RETAIN(_serverStream);
|
||||
if (_serverStream == nil)
|
||||
{
|
||||
NSLog(@"Failed to create server stream (address: %@, port: %@)", _address, _port);
|
||||
return;
|
||||
}
|
||||
|
||||
if(nil != _secure &&
|
||||
(certFile = [_secure objectForKey: @"CertificateFile"]) != nil &&
|
||||
(keyFile = [_secure objectForKey: @"KeyFile"]) != nil)
|
||||
{
|
||||
_isSecure = YES;
|
||||
[_serverStream setProperty: NSStreamSocketSecurityLevelTLSv1 forKey: NSStreamSocketSecurityLevelKey];
|
||||
[_serverStream setProperty: certFile forKey: GSTLSCertificateFile];
|
||||
[_serverStream setProperty: keyFile forKey: GSTLSCertificateKeyFile];
|
||||
}
|
||||
|
||||
_capture = [NSMutableData new];
|
||||
|
||||
[_serverStream setDelegate: self];
|
||||
[_serverStream scheduleInRunLoop: rl forMode: NSDefaultRunLoopMode];
|
||||
[_serverStream open];
|
||||
if(YES == _debug)
|
||||
{
|
||||
NSLog(@"%@: started '%@' on '%@'", self, _serverStream, [NSThread currentThread]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NSLog(@"%@: already started '%@' on '%@'", self, _serverStream, [NSThread currentThread]);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_closeServerStream
|
||||
{
|
||||
NSRunLoop *rl;
|
||||
|
||||
if(nil != _serverStream)
|
||||
{
|
||||
rl = [NSRunLoop currentRunLoop];
|
||||
|
||||
[_serverStream close];
|
||||
[_serverStream removeFromRunLoop: rl forMode: NSDefaultRunLoopMode];
|
||||
if(YES == _debug)
|
||||
{
|
||||
NSLog(@"%@: stopped server stream %@", self, _serverStream);
|
||||
}
|
||||
DESTROY(_serverStream);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_resetCycle
|
||||
{
|
||||
_written = 0;
|
||||
_doRespond = NO;
|
||||
_done = NO;
|
||||
_canRespond = NO;
|
||||
[_capture setLength: 0];
|
||||
}
|
||||
|
||||
- (void)_openIOStreams
|
||||
{
|
||||
NSRunLoop *rl;
|
||||
|
||||
if(_ip != nil && _op != nil)
|
||||
{
|
||||
rl = [NSRunLoop currentRunLoop];
|
||||
|
||||
[_ip scheduleInRunLoop: rl forMode: NSDefaultRunLoopMode];
|
||||
[_op scheduleInRunLoop: rl forMode: NSDefaultRunLoopMode];
|
||||
[_ip setDelegate: self];
|
||||
[_op setDelegate: self];
|
||||
[_ip open];
|
||||
[_op open];
|
||||
}
|
||||
else
|
||||
{
|
||||
[NSException raise: NSInternalInconsistencyException
|
||||
format: @"%@: IO streams not properly initialized", self];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_closeIOStreams
|
||||
{
|
||||
NSRunLoop *rl;
|
||||
|
||||
if(nil != _ip || nil != _op)
|
||||
{
|
||||
rl = [NSRunLoop currentRunLoop];
|
||||
|
||||
[_ip close];
|
||||
[_ip removeFromRunLoop: rl forMode: NSDefaultRunLoopMode];
|
||||
|
||||
[_op close];
|
||||
[_op removeFromRunLoop: rl forMode: NSDefaultRunLoopMode];
|
||||
|
||||
DESTROY(_ip);
|
||||
DESTROY(_op);
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)_tryCaptured
|
||||
{
|
||||
BOOL ret = NO;
|
||||
NSRange r1;
|
||||
NSRange r2;
|
||||
NSString *headers;
|
||||
NSString *tmp1;
|
||||
NSString *tmp2;
|
||||
NSUInteger contentLength;
|
||||
|
||||
// the following chunk ensures that the captured data are written only
|
||||
// when all request's bytes are read... it waits for full headers and
|
||||
// reads the Content-Length's value then waits for the number of bytes
|
||||
// equal to that value is read
|
||||
tmp1 = [[NSString alloc] initWithData: _capture
|
||||
encoding: NSUTF8StringEncoding];
|
||||
// whether the headers are read
|
||||
if((r1 = [tmp1 rangeOfString: @"\r\n\r\n"]).location != NSNotFound)
|
||||
{
|
||||
headers = [tmp1 substringToIndex: r1.location + 2];
|
||||
if((r2 = [[headers lowercaseString] rangeOfString: @"content-length:"]).location != NSNotFound)
|
||||
{
|
||||
tmp2 = [headers substringFromIndex: r2.location + r2.length]; // content-length:<tmp2><end of headers>
|
||||
if((r2 = [tmp2 rangeOfString: @"\r\n"]).location != NSNotFound)
|
||||
{
|
||||
// full line with content-length is present
|
||||
tmp2 = [tmp2 substringToIndex: r2.location]; // number of content's bytes
|
||||
contentLength = [tmp2 intValue];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
contentLength = 0; // no header 'content-length'
|
||||
}
|
||||
if(r1.location + 4 + contentLength == [_capture length]) // Did we get headers + body?
|
||||
{
|
||||
// The request has been received
|
||||
NSString *method = @"";
|
||||
NSString *query = @"";
|
||||
NSString *version = @"";
|
||||
NSString *scheme = _isSecure ? @"https" : @"http";
|
||||
NSString *path = @"";
|
||||
NSData *data;
|
||||
|
||||
// TODO: currently no checks
|
||||
r2 = [headers rangeOfString: @"\r\n"];
|
||||
while(r2.location == 0)
|
||||
{
|
||||
// ignore an empty line before the request line
|
||||
headers = [headers substringFromIndex: 2];
|
||||
r2 = [headers rangeOfString: @"\r\n"];
|
||||
}
|
||||
// the request line has been caught
|
||||
tmp2 = [tmp1 substringFromIndex: r2.location + 2]; // whole request without the first line
|
||||
data = [tmp2 dataUsingEncoding: NSUTF8StringEncoding];
|
||||
_request = [GSMimeParser documentFromData: data];
|
||||
RETAIN(_request);
|
||||
|
||||
// x-http-...
|
||||
tmp2 = [headers substringToIndex: r2.location]; // the request line
|
||||
tmp2 = [tmp2 stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceCharacterSet]];
|
||||
|
||||
// find the method
|
||||
r2 = [tmp2 rangeOfString: @" "];
|
||||
method = [[tmp2 substringToIndex: r2.location] uppercaseString];
|
||||
tmp2 = [[tmp2 substringFromIndex: r2.location + 1]
|
||||
stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceCharacterSet]];
|
||||
|
||||
r2 = [tmp2 rangeOfString: @"?"]; // path?query
|
||||
if(r2.location != NSNotFound)
|
||||
{
|
||||
// path?query
|
||||
path = [tmp2 substringToIndex: r2.location];
|
||||
tmp2 = [tmp2 substringFromIndex: r2.location + 1]; // without '?'
|
||||
r2 = [tmp2 rangeOfString: @" "];
|
||||
query = [tmp2 substringToIndex: r2.location];
|
||||
}
|
||||
else
|
||||
{
|
||||
// only path
|
||||
r2 = [tmp2 rangeOfString: @" "];
|
||||
path = [tmp2 substringToIndex: r2.location];
|
||||
}
|
||||
tmp2 = [[tmp2 substringFromIndex: r2.location + 1]
|
||||
stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceCharacterSet]];
|
||||
// tmp2 == 'HTTP/<version>'
|
||||
version = [tmp2 substringFromIndex: 5];
|
||||
|
||||
|
||||
[_request setHeader: @"x-http-method"
|
||||
value: method
|
||||
parameters: nil];
|
||||
|
||||
[_request setHeader: @"x-http-path"
|
||||
value: path
|
||||
parameters: nil];
|
||||
|
||||
[_request setHeader: @"x-http-query"
|
||||
value: query
|
||||
parameters: nil];
|
||||
|
||||
[_request setHeader: @"x-http-scheme"
|
||||
value: scheme
|
||||
parameters: nil];
|
||||
|
||||
[_request setHeader: @"x-http-version"
|
||||
value: version
|
||||
parameters: nil];
|
||||
|
||||
if(YES == _debug)
|
||||
{
|
||||
NSLog(@"%@: got request\n%@", self, _request);
|
||||
}
|
||||
_response = [GSMimeDocument new];
|
||||
if(nil != _delegate && [_delegate respondsToSelector: @selector(processRequest:response:for:)])
|
||||
{
|
||||
ret = [_delegate processRequest: _request response: _response for: self];
|
||||
}
|
||||
if(!ret)
|
||||
{
|
||||
DESTROY(_response);
|
||||
_response = [GSMimeDocument new];
|
||||
[_response setHeader: @"HTTP" value: @"HTTP/1.1 204 No Content" parameters: nil];
|
||||
[_response setHeader: @"Content-Length" value: @"0" parameters: nil];
|
||||
ret = YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
DESTROY(tmp1);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@end /* SimpleWebServer (Private) */
|
129
Tests/base/NSURLConnection/Helpers/TestCase.h
Normal file
129
Tests/base/NSURLConnection/Helpers/TestCase.h
Normal file
|
@ -0,0 +1,129 @@
|
|||
/* -*- objc -*-
|
||||
*
|
||||
* Author: Sergei Golovin <Golovin.SV@gmail.com>
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
/* the initial (reset) state of any flag set */
|
||||
#define NORESULTS 0
|
||||
|
||||
/**
|
||||
* A class representing some test case and implementing
|
||||
* the protocol TestProgress allows descendants to have two sets
|
||||
* of flags: the reference one and the actual one.
|
||||
* The reference flag set is what the class's instance's
|
||||
* state supposed to be if the test was successfull.
|
||||
* The actual flag set is the current state of the class's
|
||||
* instance during execution of the test.
|
||||
* Both can be used in conjuction to decide about test's
|
||||
* success or fail by checking at the end of the test
|
||||
* whether the sets are identical.
|
||||
*/
|
||||
@protocol TestProgress
|
||||
|
||||
/**
|
||||
* Resets the actual flags to the pristine state (NORESULTS).
|
||||
*/
|
||||
- (void)resetFlags;
|
||||
|
||||
/**
|
||||
* Sets the actual flags defined by the supplied mask.
|
||||
*/
|
||||
- (void)setFlags:(NSUInteger)mask;
|
||||
|
||||
/**
|
||||
* Unset the actual flags defined by the supplied mask.
|
||||
*/
|
||||
- (void)unsetFlags:(NSUInteger)mask;
|
||||
|
||||
/**
|
||||
* Returns YES if the actual flags defined by the supplied mask are
|
||||
* set.
|
||||
*/
|
||||
- (BOOL)isFlagSet:(NSUInteger)mask;
|
||||
|
||||
/**
|
||||
* Resets the reference flags to the pristine state (NORESULTS).
|
||||
*/
|
||||
- (void)resetReferenceFlags;
|
||||
|
||||
/**
|
||||
* Stores the supplied mask as the reference flags state.
|
||||
* The actual flags state equal to the reference one signifies
|
||||
* a success.
|
||||
*/
|
||||
- (void)setReferenceFlags:(NSUInteger)mask;
|
||||
|
||||
/**
|
||||
* Unset the reference flags defined by the supplied mask.
|
||||
*/
|
||||
- (void)unsetReferenceFlags:(NSUInteger)mask;
|
||||
|
||||
/**
|
||||
* Returns YES if the reference flags defined by the supplied mask are
|
||||
* set.
|
||||
*/
|
||||
- (BOOL)isReferenceFlagSet:(NSUInteger)mask;
|
||||
|
||||
/**
|
||||
* Returns YES if all conditions are met (by default if the actual flag set
|
||||
* is equal to the reference flag set).
|
||||
*/
|
||||
- (BOOL)isSuccess;
|
||||
|
||||
@end /* TestProgress */
|
||||
|
||||
/**
|
||||
* The abstract class representing a test case and implementing
|
||||
* the protocol TestProgress.
|
||||
*/
|
||||
@interface TestCase : NSObject <TestProgress>
|
||||
{
|
||||
/* the flags indicating test progress (the actual one) */
|
||||
NSUInteger _flags;
|
||||
/* the reference flag set signifying the test's successful state */
|
||||
NSUInteger _refFlags;
|
||||
/* the particular flag indicating a failure not related to the test */
|
||||
BOOL _failed;
|
||||
/* the particular flag signifying the test has been done */
|
||||
BOOL _done;
|
||||
/* keeps the extra which can likely be of NSDictionary class in which case
|
||||
* it's keys hold parameters of the test */
|
||||
id _extra;
|
||||
/* the debug mode flag */
|
||||
BOOL _debug;
|
||||
}
|
||||
|
||||
- (id)init;
|
||||
|
||||
/**
|
||||
* A descendant class can place in the own implementation of the method
|
||||
* some preliminary code needed to conduct the test case. The argument
|
||||
* 'extra' is for future enhancements. The extra is only retained within
|
||||
* the method for descendants (the abstarct class doesn't do anything
|
||||
* with this argument).
|
||||
* So descendant's implementation MUST call the super's one.
|
||||
*/
|
||||
- (void)setUpTest:(id)extra;
|
||||
|
||||
/**
|
||||
* A descendant class should place the code conducting the test in
|
||||
* the own implementation of the method. The argument 'extra' is for
|
||||
* future enhancements.
|
||||
*/
|
||||
- (void)startTest:(id)extra;
|
||||
|
||||
/**
|
||||
* A descendant class can place in the own implementation of the method
|
||||
* some cleaning code. The argument 'extra' is for future enhancements.
|
||||
* Descendant's implementation MUST call the super's one.
|
||||
*/
|
||||
- (void)tearDownTest:(id)extra;
|
||||
|
||||
/**
|
||||
* Raises the verbosity level if supplied with YES.
|
||||
*/
|
||||
- (void)setDebug:(BOOL)flag;
|
||||
|
||||
@end /* TestCase */
|
106
Tests/base/NSURLConnection/Helpers/TestCase.m
Normal file
106
Tests/base/NSURLConnection/Helpers/TestCase.m
Normal file
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* Author: Sergei Golovin <Golovin.SV@gmail.com>
|
||||
*/
|
||||
|
||||
#import "TestCase.h"
|
||||
|
||||
@implementation TestCase
|
||||
|
||||
- (id)init
|
||||
{
|
||||
if((self = [super init]) != nil)
|
||||
{
|
||||
[self resetFlags];
|
||||
[self setReferenceFlags: NORESULTS];
|
||||
_failed = NO;
|
||||
_done = NO;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[self tearDownTest: _extra];
|
||||
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
/* TestProgress */
|
||||
- (void)resetFlags
|
||||
{
|
||||
_flags = NORESULTS;
|
||||
}
|
||||
|
||||
- (void)setFlags:(NSUInteger)mask
|
||||
{
|
||||
_flags = _flags | mask;
|
||||
}
|
||||
|
||||
- (void)unsetFlags:(NSUInteger)mask
|
||||
{
|
||||
_flags = _flags & ~mask;
|
||||
}
|
||||
|
||||
- (BOOL)isFlagSet:(NSUInteger)mask
|
||||
{
|
||||
return ((_flags & mask) == mask);
|
||||
}
|
||||
|
||||
- (void)resetReferenceFlags
|
||||
{
|
||||
_refFlags = NORESULTS;
|
||||
}
|
||||
|
||||
- (void)setReferenceFlags:(NSUInteger)mask
|
||||
{
|
||||
_refFlags = _refFlags | mask;
|
||||
}
|
||||
|
||||
- (void)unsetReferenceFlags:(NSUInteger)mask
|
||||
{
|
||||
_refFlags = _refFlags & ~mask;
|
||||
}
|
||||
|
||||
- (BOOL)isReferenceFlagSet:(NSUInteger)mask
|
||||
{
|
||||
return ((_refFlags & mask) == mask);
|
||||
}
|
||||
|
||||
- (BOOL)isSuccess
|
||||
{
|
||||
if(!_failed && (_flags == _refFlags))
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
/* end of TestProgress */
|
||||
|
||||
- (void)setUpTest:(id)extra
|
||||
{
|
||||
[self resetFlags];
|
||||
[self resetReferenceFlags];
|
||||
_failed = NO;
|
||||
_done = NO;
|
||||
|
||||
ASSIGN(_extra, extra);
|
||||
}
|
||||
|
||||
- (void)startTest:(id)extra
|
||||
{
|
||||
// does nothing
|
||||
}
|
||||
|
||||
- (void)tearDownTest:(id)extra
|
||||
{
|
||||
DESTROY(_extra);
|
||||
}
|
||||
|
||||
- (void)setDebug:(BOOL)flag
|
||||
{
|
||||
_debug = flag;
|
||||
}
|
||||
|
||||
@end /* TestCase */
|
274
Tests/base/NSURLConnection/Helpers/TestWebServer.h
Normal file
274
Tests/base/NSURLConnection/Helpers/TestWebServer.h
Normal file
|
@ -0,0 +1,274 @@
|
|||
|
||||
/** -*- objc -*-
|
||||
*
|
||||
* Author: Sergei Golovin <Golovin.SV@gmail.com>
|
||||
*
|
||||
* TestWebServer is intended to be used in the tests where a web service is
|
||||
* needed. The class's instance is instantiated with the method
|
||||
* -[initWithAddress:port:mode:extra] and then started with the call -[start:].
|
||||
* When started the TestWebServer starts in it's turn a SimpleWebServer instance
|
||||
* in the same thread unless it is initialized with the argument 'mode' set to
|
||||
* 'YES' to run it in a detached thread.
|
||||
*
|
||||
* It is designed to call the delegate's callbacks (if implemented) of
|
||||
* the TestWebServerDelegate protocol for every request made to
|
||||
* the SimpleWebServer's assigned address on the SimpleWebServer's assigned port
|
||||
* (by default 127.0.0.1 and 54321 respectively). However it currently doesn't
|
||||
* handle any request on it's own. Instead the class uses a collection of
|
||||
* handlers. Every handler makes a custom response. So the TestWebServer only
|
||||
* has to dispatch a request to a corresponding handler and then to send back
|
||||
* the handler's response to the SimpleWebServer instance. See RequestHandler
|
||||
* for more information.
|
||||
*
|
||||
* TestWebServer can be supplied with additional parameters via the argument
|
||||
* 'extra' of the initializer -[initWithAddress:port:mode:extra].
|
||||
* See that method's description for the currently supported key-value pairs.
|
||||
*
|
||||
* The pattern of use:
|
||||
* ----------------------------------------------------------------------------
|
||||
* NSDictionary *extra = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
* @"https", @"Protocol",
|
||||
* nil];
|
||||
* TestWebServer *server = [[TestWebServer alloc] initWithAddress: @"127.0.0.1"
|
||||
* port: @"54321"
|
||||
* mode: NO
|
||||
* extra: extra];
|
||||
* ADelegateClass *del = [ADelegateClass new];
|
||||
* [server setDelegate: del];
|
||||
* [server start: extra];
|
||||
* ....
|
||||
* <runloop>
|
||||
* ....
|
||||
* [server stop];
|
||||
* DESTROY(server);
|
||||
* DESTROY(del);
|
||||
* ----------------------------------------------------------------------------
|
||||
*
|
||||
*
|
||||
* The TestWebServer can response to the following resource paths:
|
||||
*
|
||||
* /index
|
||||
* displays the page with all supported resources
|
||||
* /withoutauth/
|
||||
* if it is a part of the request's URL then the TestWebServer
|
||||
* will NOT check authentication/authorization of the client.
|
||||
*
|
||||
* /204/
|
||||
* /301/
|
||||
* /400/
|
||||
* /401/
|
||||
* /402/
|
||||
* /404/
|
||||
* /405/
|
||||
* /409/
|
||||
* /500/
|
||||
* /505/
|
||||
* /507/
|
||||
* a try to access to this resources will lead the TestWebServer
|
||||
* to produce one of the pre-defined responses with the status code
|
||||
* is equal to the corresponding number.
|
||||
* The pre-defined responses are:
|
||||
* 204 "" (empty string)
|
||||
* 301 "Redirect to <URL>"
|
||||
* Returns in the header 'Location' a new <URL> which by default
|
||||
* has the same protocol, address but the port is incremented by
|
||||
* 1 (e.g. http://localhost:54322/ with other parameters set to
|
||||
* their default values).
|
||||
* 400 "You have issued a request with invalid data"
|
||||
* 401 "Invalid login or password"
|
||||
* 403 "Check your balance"
|
||||
* 404 "The resource you are trying to access is not found"
|
||||
* 405 "You can't do that"
|
||||
* 409 "The resource you are trying to access is busy"
|
||||
* 500 "System error"
|
||||
* 505 "There is network protocol inconsistency"
|
||||
* 507 "Insufficient storage"
|
||||
*
|
||||
* If you want more verbosity call the method -[setDebug: YES].
|
||||
*
|
||||
*/
|
||||
|
||||
#import "SimpleWebServer.h"
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@class RequestHandler;
|
||||
|
||||
@interface TestWebServer : NSObject <SimpleWebServerDelegate>
|
||||
{
|
||||
/* the debug mode flag */
|
||||
BOOL _debug;
|
||||
/* the IP address to attach to... see DEFAULTADDRESS at the beginning
|
||||
* of the implementaion */
|
||||
NSString *_address;
|
||||
/* the port to listen on... see DEFAULTPORT in the beginning
|
||||
* of the implementaion */
|
||||
NSString *_port;
|
||||
/* whether the TestWebServer listens for HTTPS requests */
|
||||
BOOL _isSecure;
|
||||
/* the login for basic authentication */
|
||||
NSString *_login;
|
||||
/* the password for basic authentication */
|
||||
NSString *_password;
|
||||
/* holds the extra argument which is for a future use...
|
||||
* Currently it is expected to be a dictionary with predetermined
|
||||
* keys ( see the description of -[initWithAddress:port:mode:extra:]) */
|
||||
id _extra;
|
||||
/* the flag used as a trigger to stop the detached thread */
|
||||
BOOL _threadToQuit;
|
||||
/* holds the SimpleWebServer accepting incoming connections */
|
||||
SimpleWebServer *_server;
|
||||
/* holds the detached thread the SimpleWebServer is running on...
|
||||
* nil if it runs in the same thread as the calling code */
|
||||
NSThread *_serverThread;
|
||||
/* the delegate ... NOT RETAINED...
|
||||
* see below the protocol TestWebServerDelegate */
|
||||
id _delegate;
|
||||
/* the lock used for synchronization between threads mainly
|
||||
* to wait for the SimpleWebServer is started/stopped...
|
||||
* the condition list:
|
||||
* READY - the initial condition
|
||||
* STARTED - the SimpleWebServer has been started;
|
||||
* STOPPED - the SimpleWebServer has been stopped;
|
||||
*/
|
||||
NSConditionLock *_lock;
|
||||
/* the traversal map used to pick a right handler from the request's path */
|
||||
NSMutableDictionary *_traversalMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the intance with default parameters.
|
||||
*/
|
||||
- (id)init;
|
||||
|
||||
/**
|
||||
* Initializes an instance with it's SimpleWebServer accepting connection on
|
||||
* the specified address and port. The mode decides whether to run
|
||||
* the SimpleWebServer on the detached thread (NO means it won't be detached).
|
||||
* The argument extra is for future enhancement and could be of the class
|
||||
* NSDictionary whose key-value pairs set various parameters.
|
||||
* The current code supports the following extra keys:
|
||||
* 'Login' - the login for basic authentication
|
||||
* 'Password' - the password for basic authentication
|
||||
* 'Protocol' - 'http' means waiting for HTTP requests
|
||||
* 'https' - for HTTPS requests
|
||||
*/
|
||||
- (id)initWithAddress:(NSString *)address
|
||||
port:(NSString *)port
|
||||
mode:(BOOL)detached
|
||||
extra:(id)extra;
|
||||
- (void)dealloc;
|
||||
|
||||
/**
|
||||
* Starts the SimpleWebServer accepting incoming connections.
|
||||
* The supplied argument is for future enhancement and currently has no meaning.
|
||||
* It isn't retained by the method.
|
||||
*/
|
||||
- (void)start:(id)extra;
|
||||
|
||||
/**
|
||||
* Stops the SimpleWebServer. The instance ceases to accept incoming connections.
|
||||
*/
|
||||
- (void)stop;
|
||||
|
||||
/* SimpleWebServerDelegate */
|
||||
/**
|
||||
* The main job of request-response cycle is done here. The method is called
|
||||
* when all request's bytes are read. It must be supplied with the incoming
|
||||
* request and the response document which is about to be sent back to the web
|
||||
* client. The handling code within the method would change the supplied response.
|
||||
*/
|
||||
- (BOOL) processRequest:(GSMimeDocument *)request
|
||||
response:(GSMimeDocument *)response
|
||||
for:(SimpleWebServer *)server;
|
||||
/* end of SimpleWebServerDelegate */
|
||||
|
||||
/* getters */
|
||||
|
||||
/**
|
||||
* Returns the address which SimpleWebServer is bound to.
|
||||
*/
|
||||
- (NSString *)address;
|
||||
|
||||
/**
|
||||
* Returns the port which the SimpleWebServer listens on.
|
||||
*/
|
||||
- (NSString *)port;
|
||||
|
||||
/**
|
||||
* Returns the login for basic authentication.
|
||||
*/
|
||||
- (NSString *)login;
|
||||
|
||||
/**
|
||||
* Returns the password for basic authentication.
|
||||
*/
|
||||
- (NSString *)password;
|
||||
|
||||
/**
|
||||
* Returns YES if the instance waits for HTTPS requests.
|
||||
*/
|
||||
- (BOOL)isSecure;
|
||||
|
||||
/* end of getters */
|
||||
|
||||
/* setters */
|
||||
|
||||
/**
|
||||
* Sets the delegate implementing TestWebServerDelegate protocol.
|
||||
* The argument isn't retained by the method.
|
||||
*/
|
||||
- (void)setDelegate:(id)delegate;
|
||||
|
||||
/**
|
||||
* Sets the debug mode (more verbose).
|
||||
*/
|
||||
- (void)setDebug:(BOOL)mode;
|
||||
|
||||
/* end of setters */
|
||||
|
||||
@end /* TestWebServer */
|
||||
|
||||
/**
|
||||
* The protocol's methods are called during processing of a request.
|
||||
*/
|
||||
@protocol TestWebServerDelegate
|
||||
|
||||
/**
|
||||
* Called by the handler when it has got the supplied request
|
||||
* from the supplied TestWebServer instance.
|
||||
*/
|
||||
- (void)handler:(id)handler
|
||||
gotRequest:(GSMimeDocument *)request
|
||||
with:(TestWebServer *)server;
|
||||
|
||||
/**
|
||||
* Called by the handler when it is going to send the response
|
||||
* on an unauthorized request (an invalid request or no credentials are supplied)
|
||||
* via the supplied TestWebServer.
|
||||
*/
|
||||
- (void)handler:(id)handler
|
||||
willSendUnauthorized:(GSMimeDocument *)response
|
||||
with:(TestWebServer *)server;
|
||||
|
||||
/**
|
||||
* Called by the handler when it has got the supplied request
|
||||
* with valid credentials from the supplied TestWebServer instance.
|
||||
*/
|
||||
- (void)handler:(id)handler
|
||||
gotAuthorized:(GSMimeDocument *)request
|
||||
with:(TestWebServer *)server;
|
||||
|
||||
/**
|
||||
* Called by the handler when it is going to send the response
|
||||
* on any request except an unauthorized one via the supplied TestWebServer.
|
||||
*/
|
||||
- (void)handler:(id)handler
|
||||
willSend:(GSMimeDocument *)response
|
||||
with:(TestWebServer *)server;
|
||||
|
||||
/**
|
||||
* Called when the timeout is exceeded.
|
||||
*/
|
||||
- (void)timeoutExceededByHandler:(id)handler;
|
||||
|
||||
@end /* TestWebServerDelegate */
|
468
Tests/base/NSURLConnection/Helpers/TestWebServer.m
Normal file
468
Tests/base/NSURLConnection/Helpers/TestWebServer.m
Normal file
|
@ -0,0 +1,468 @@
|
|||
/*
|
||||
* Author: Sergei Golovin <Golovin.SV@gmail.com>
|
||||
*/
|
||||
|
||||
#import "TestWebServer.h"
|
||||
#import "RequestHandler.h"
|
||||
|
||||
@interface TestWebServer (Private)
|
||||
|
||||
/**
|
||||
* Starts the detached thread. It is the entry point of the thread.
|
||||
*/
|
||||
- (void)_startDetached:(id)extra;
|
||||
|
||||
/**
|
||||
* Starts the SimpleWebServer.
|
||||
*/
|
||||
- (void)_startHTTPServer:(id)extra;
|
||||
|
||||
/**
|
||||
* Stops the SimpleWebServer.
|
||||
*/
|
||||
- (void)_stopHTTPServer;
|
||||
|
||||
|
||||
@end
|
||||
|
||||
/* default 'constants' */
|
||||
#define DEFAULTADDRESS @"127.0.0.1"
|
||||
#define DEFAULTPORT @"54321"
|
||||
#define DEFAULTMODE NO
|
||||
#define DEFAULTLOGIN @"login"
|
||||
#define DEFAULTPASSWORD @"password"
|
||||
|
||||
/* lock's conditions */
|
||||
#define READY 0 /* the initial condition */
|
||||
#define STARTED 1 /* the SimpleWebServer has been started */
|
||||
#define STOPPED 2 /* the SimpleWebServer has been stopped */
|
||||
|
||||
/* the time step for the runloop */
|
||||
#define TIMING 0.1
|
||||
/* the maximum duration of running of the SimpleWebServer instance
|
||||
after which the server must be shut down */
|
||||
#define MAXDURATION 3.0
|
||||
|
||||
@implementation TestWebServer
|
||||
|
||||
- (id)init
|
||||
{
|
||||
return [self initWithAddress: DEFAULTADDRESS
|
||||
port: DEFAULTPORT
|
||||
mode: DEFAULTMODE
|
||||
extra: nil];
|
||||
}
|
||||
|
||||
- (id)initWithAddress:(NSString *)address
|
||||
port:(NSString *)port
|
||||
mode:(BOOL)detached
|
||||
extra:(id)extra
|
||||
{
|
||||
if((self = [super init]) != nil)
|
||||
{
|
||||
_debug = NO;
|
||||
_address = [[NSHost hostWithName: address] address];
|
||||
RETAIN(_address);
|
||||
ASSIGN(_port, port);
|
||||
ASSIGN(_extra, extra);
|
||||
_password = nil;
|
||||
_login = nil;
|
||||
_isSecure = NO;
|
||||
|
||||
if([extra isKindOfClass: [NSDictionary class]])
|
||||
{
|
||||
NSDictionary *d = extra;
|
||||
NSString *proto;
|
||||
|
||||
if((_password = [d objectForKey: @"Password"]) != nil)
|
||||
{
|
||||
RETAIN(_password);
|
||||
}
|
||||
if((_login = [d objectForKey: @"Login"]) != nil)
|
||||
{
|
||||
RETAIN(_login);
|
||||
}
|
||||
if((proto = [d objectForKey: @"Protocol"]) != nil &&
|
||||
[[proto lowercaseString] isEqualToString: @"https"])
|
||||
{
|
||||
_isSecure = YES;
|
||||
}
|
||||
}
|
||||
|
||||
if(nil == _login)
|
||||
{
|
||||
_login = DEFAULTLOGIN; RETAIN(_login);
|
||||
}
|
||||
if(nil == _password)
|
||||
{
|
||||
_password = DEFAULTPASSWORD; RETAIN(_password);
|
||||
}
|
||||
|
||||
_traversalMap = [[NSMutableDictionary alloc]
|
||||
initWithObjectsAndKeys:
|
||||
[Handler200 class], @"200",
|
||||
[Handler204 class], @"204",
|
||||
[Handler301 class], @"301",
|
||||
[Handler400 class], @"400",
|
||||
[Handler401 class], @"401",
|
||||
[Handler402 class], @"402",
|
||||
[Handler404 class], @"404",
|
||||
[Handler405 class], @"405",
|
||||
[Handler409 class], @"409",
|
||||
[Handler500 class], @"500",
|
||||
[Handler505 class], @"505",
|
||||
[Handler507 class], @"507",
|
||||
[HandlerIndex class], @"index",
|
||||
nil];
|
||||
|
||||
_lock = [[NSConditionLock alloc] initWithCondition: READY];
|
||||
_threadToQuit = NO;
|
||||
if(detached)
|
||||
{
|
||||
_serverThread = [[NSThread alloc] initWithTarget: self
|
||||
selector: @selector(_startDetached:)
|
||||
object: extra];
|
||||
}
|
||||
else
|
||||
{
|
||||
_serverThread = nil;
|
||||
}
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
DESTROY(_lock);
|
||||
DESTROY(_address);
|
||||
DESTROY(_port);
|
||||
DESTROY(_extra);
|
||||
DESTROY(_login);
|
||||
DESTROY(_password);
|
||||
_delegate = nil;
|
||||
DESTROY(_traversalMap);
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (void)start:(id)extra
|
||||
{
|
||||
if([_server port] != nil)
|
||||
{
|
||||
if(YES == _debug)
|
||||
{
|
||||
NSWarnMLog(@"SimpleWebServer already started");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(nil != _serverThread)
|
||||
{
|
||||
if(![_serverThread isExecuting])
|
||||
{
|
||||
NSTimeInterval duration = 0.0;
|
||||
[_serverThread start];
|
||||
|
||||
// wait for the SimpleWebServer is started
|
||||
while(![_lock tryLockWhenCondition: STARTED] &&
|
||||
duration < MAXDURATION)
|
||||
{
|
||||
[[NSRunLoop currentRunLoop]
|
||||
runUntilDate: [NSDate dateWithTimeIntervalSinceNow: TIMING]];
|
||||
duration += TIMING;
|
||||
}
|
||||
[_lock unlock];
|
||||
if(duration >= MAXDURATION &&
|
||||
nil != _delegate &&
|
||||
[_delegate respondsToSelector: @selector(timeoutExceededByHandler:)])
|
||||
{
|
||||
[_delegate timeoutExceededByHandler: self];
|
||||
[self stop];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NSWarnMLog(@"the detached thread is already started");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
[self _startHTTPServer: extra];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)stop
|
||||
{
|
||||
if([_server port] == nil)
|
||||
{
|
||||
if(YES == _debug)
|
||||
{
|
||||
NSWarnMLog(@"SimpleWebServer already stopped");
|
||||
}
|
||||
return;
|
||||
}
|
||||
if(nil != _serverThread)
|
||||
{
|
||||
if([_serverThread isExecuting])
|
||||
{
|
||||
NSTimeInterval duration = 0.0;
|
||||
_threadToQuit = YES; // this makes the detached thread quiting from it's runloop
|
||||
|
||||
// wait for the SimpleWebServer is stopped
|
||||
while(![_lock tryLockWhenCondition: STOPPED] &&
|
||||
duration < MAXDURATION)
|
||||
{
|
||||
[[NSRunLoop currentRunLoop]
|
||||
runUntilDate: [NSDate dateWithTimeIntervalSinceNow: TIMING]];
|
||||
duration += TIMING;
|
||||
}
|
||||
[_lock unlockWithCondition: READY];
|
||||
if(duration >= MAXDURATION &&
|
||||
nil != _delegate &&
|
||||
[_delegate respondsToSelector: @selector(timeoutExceededByHandler:)])
|
||||
{
|
||||
[_delegate timeoutExceededByHandler: self];
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
[self _stopHTTPServer];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL) processRequest:(GSMimeDocument *)request
|
||||
response:(GSMimeDocument *)response
|
||||
for:(SimpleWebServer *)server
|
||||
{
|
||||
NSString *path;
|
||||
NSString *component;
|
||||
NSEnumerator *en;
|
||||
BOOL ret = NO;
|
||||
id handler;
|
||||
|
||||
// analyze the path to infer what the client wants
|
||||
path = [[request headerNamed:@"x-http-path"] value];
|
||||
|
||||
// traverse over the path to search the right handler
|
||||
en = [[path pathComponents] objectEnumerator];
|
||||
while((component = [en nextObject]) != nil)
|
||||
{
|
||||
if((handler = [_traversalMap objectForKey: component]) != nil)
|
||||
{
|
||||
// the handler has been found
|
||||
break; // while
|
||||
}
|
||||
}
|
||||
|
||||
if(nil == component)
|
||||
{
|
||||
// no component found... default 204
|
||||
component = @"204";
|
||||
handler = [_traversalMap objectForKey: component];
|
||||
}
|
||||
|
||||
if(handler == [handler class])
|
||||
{
|
||||
// it is a Class not an instance... instantiate and substitute
|
||||
// instead of the Class
|
||||
Class cls = (Class)handler;
|
||||
handler = [[cls alloc] init];
|
||||
[_traversalMap setObject: handler forKey: component];
|
||||
}
|
||||
|
||||
// set the handler
|
||||
[handler setDelegate: _delegate];
|
||||
[handler setLogin: _login];
|
||||
[handler setPassword: _password];
|
||||
if([handler respondsToSelector: @selector(setURLString:)])
|
||||
{
|
||||
if([handler isKindOfClass: [HandlerIndex class]])
|
||||
{
|
||||
NSString *urlString = [NSString stringWithFormat: @"%@://%@:%@/",
|
||||
_isSecure ? @"https" : @"http",
|
||||
_address,
|
||||
_port];
|
||||
|
||||
[(HandlerIndex *)handler setURLString: urlString];
|
||||
}
|
||||
else if([handler isKindOfClass: [Handler301 class]])
|
||||
{
|
||||
// by default http://localhost:54322/
|
||||
NSString *port = [NSString stringWithFormat: @"%u", [_port intValue] + 1]; // the TestWebServer's port + 1
|
||||
NSString *urlString = [NSString stringWithFormat: @"%@://%@:%@/",
|
||||
_isSecure ? @"https" : @"http",
|
||||
_address,
|
||||
port];
|
||||
|
||||
[(Handler301 *)handler setURLString: urlString];
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: add session-related conditions here
|
||||
[handler prehandleRequest: request
|
||||
response: response
|
||||
for: self];
|
||||
|
||||
// the main job
|
||||
ret = [handler handleRequest: request
|
||||
response: response
|
||||
for: self];
|
||||
// TODO: analyze 'ret'... if NO a default handler must be called
|
||||
|
||||
// TODO: add session-related conditions here
|
||||
[handler posthandleRequest: request
|
||||
response: response
|
||||
for: self];
|
||||
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* end of SimpleWebServer delegate */
|
||||
|
||||
|
||||
/* getters */
|
||||
|
||||
- (NSString *)address
|
||||
{
|
||||
return _address;
|
||||
}
|
||||
|
||||
- (NSString *)port
|
||||
{
|
||||
return _port;
|
||||
}
|
||||
|
||||
- (NSString *)login
|
||||
{
|
||||
return _login;
|
||||
}
|
||||
|
||||
- (NSString *)password
|
||||
{
|
||||
return _password;
|
||||
}
|
||||
|
||||
- (BOOL)isSecure
|
||||
{
|
||||
return _isSecure;
|
||||
}
|
||||
|
||||
/* end of getters */
|
||||
|
||||
/* setters */
|
||||
- (void)setDelegate:(id)delegate
|
||||
{
|
||||
_delegate = delegate;
|
||||
}
|
||||
|
||||
- (void)setDebug:(BOOL)mode
|
||||
{
|
||||
_debug = mode;
|
||||
}
|
||||
|
||||
/* end of setters */
|
||||
|
||||
@end /* TestWebServer */
|
||||
|
||||
|
||||
@implementation TestWebServer (Private)
|
||||
|
||||
- (void)_startHTTPServer:(id)extra
|
||||
{
|
||||
NSString *certPath;
|
||||
NSString *keyPath;
|
||||
NSDictionary *secure = nil;
|
||||
BOOL status;
|
||||
|
||||
_server = [SimpleWebServer new];
|
||||
[_server setDebug: _debug];
|
||||
[_server setDelegate: self];
|
||||
if(_isSecure)
|
||||
{
|
||||
if([_address isEqualToString: @"127.0.0.1"] ||
|
||||
[_address isEqualToString: @"localhost"])
|
||||
{
|
||||
certPath = [[NSBundle bundleForClass: [self class]]
|
||||
pathForResource: @"testCert"
|
||||
ofType: @"pem"];
|
||||
keyPath = [[NSBundle bundleForClass: [self class]]
|
||||
pathForResource: @"testKey"
|
||||
ofType: @"pem"];
|
||||
secure = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
certPath, @"CertificateFile",
|
||||
keyPath, @"KeyFile",
|
||||
nil];
|
||||
}
|
||||
else
|
||||
{
|
||||
// NOTE: generate corresponding certificates for any address differing
|
||||
// from 127.0.0.1
|
||||
}
|
||||
}
|
||||
|
||||
status = [_server setAddress: _address port: _port secure: secure]; // run the server
|
||||
if(!status)
|
||||
{
|
||||
[NSException raise: NSInternalInconsistencyException
|
||||
format: @"The server hasn't run. Perhaps the port %@ is invalid", DEFAULTPORT];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_stopHTTPServer
|
||||
{
|
||||
if(nil != _server && [_server port] != nil)
|
||||
{
|
||||
[_server stop]; // shut down the server
|
||||
if(YES == _debug)
|
||||
{
|
||||
NSLog(@"%@: stopped SimpleWebServer %@", self, _server);
|
||||
}
|
||||
DESTROY(_server);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_startDetached:(id)extra
|
||||
{
|
||||
CREATE_AUTORELEASE_POOL(arp);
|
||||
NSTimeInterval duration = 0.0;
|
||||
|
||||
[_lock lockWhenCondition: READY];
|
||||
[self _startHTTPServer: extra];
|
||||
[_lock unlockWithCondition: STARTED];
|
||||
|
||||
if(YES == _debug)
|
||||
{
|
||||
NSLog(@"%@: enter into runloop in detached thread %@", self, [NSThread currentThread]);
|
||||
}
|
||||
|
||||
while(!_threadToQuit && duration < MAXDURATION)
|
||||
{
|
||||
[[NSRunLoop currentRunLoop]
|
||||
runUntilDate: [NSDate dateWithTimeIntervalSinceNow: TIMING]];
|
||||
duration += TIMING;
|
||||
}
|
||||
|
||||
if(YES == _debug)
|
||||
{
|
||||
NSLog(@"%@: exit from runloop in detached thread %@", self, [NSThread currentThread]);
|
||||
}
|
||||
|
||||
if(duration >= MAXDURATION &&
|
||||
nil != _delegate &&
|
||||
[_delegate respondsToSelector: @selector(timeoutExceededByHandler:)])
|
||||
{
|
||||
[_delegate timeoutExceededByHandler: self];
|
||||
}
|
||||
|
||||
[_lock lockWhenCondition: STARTED];
|
||||
[self _stopHTTPServer];
|
||||
[_lock unlockWithCondition: STOPPED];
|
||||
|
||||
DESTROY(arp);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@end /* TestWebServer (Private) */
|
23
Tests/base/NSURLConnection/Helpers/testCert.pem
Normal file
23
Tests/base/NSURLConnection/Helpers/testCert.pem
Normal file
|
@ -0,0 +1,23 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIID6TCCAtGgAwIBAgIJAP02s2/x3i8ZMA0GCSqGSIb3DQEBCwUAMIGKMQswCQYD
|
||||
VQQGEwJYWDEOMAwGA1UECAwFV29ybGQxEDAOBgNVBAcMB0dOVXN0ZXAxITAfBgNV
|
||||
BAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDESMBAGA1UEAwwJbG9jYWxob3N0
|
||||
MSIwIAYJKoZIhvcNAQkBFhNnbnVzdGVwLWRldkBnbnUub3JnMB4XDTE0MDgwNzEx
|
||||
MTIxN1oXDTI0MDgwNDExMTIxN1owgYoxCzAJBgNVBAYTAlhYMQ4wDAYDVQQIDAVX
|
||||
b3JsZDEQMA4GA1UEBwwHR05Vc3RlcDEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0
|
||||
cyBQdHkgTHRkMRIwEAYDVQQDDAlsb2NhbGhvc3QxIjAgBgkqhkiG9w0BCQEWE2du
|
||||
dXN0ZXAtZGV2QGdudS5vcmcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
|
||||
AQDxFVEHh137hyl0juYvbXuAOUIXRSVwk92mAGJIIn0g0Dm6KIAJW1EGR5LeHY3L
|
||||
vrkxEAvGxb7Ypqtg1F4OcwoZE/1Y0xKjHBnqtMJcw7DgN9F1dIkQ9HxHkYPiHzTF
|
||||
d7floomsjyt0BcqAqE1Qf0ahsnveq9E6KTIYRTZ91RGHQrAW4KBxFM30ieHYQYdn
|
||||
2vDgH/8wyLjwQ+89P5SrBJdt+eKHPjHvjs1WZe0i660hvLa19l/DQ5ZxlV5LJ6tZ
|
||||
yjvr+OjT9tE86r9n34vk+ZZBCQwvzZ+dHwLpufz3VgPap54bCCLY21BZj15nqITN
|
||||
k+8XJz+aavFe3zdMvT9cGiZNAgMBAAGjUDBOMB0GA1UdDgQWBBRB6fstnCsIvg3r
|
||||
9f3EmiZhLS867TAfBgNVHSMEGDAWgBRB6fstnCsIvg3r9f3EmiZhLS867TAMBgNV
|
||||
HRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQC0fy/lWgBZRTXfAUOfZp5sWJcQ
|
||||
mQKQiPfXYxiUALq6iI1SyQq90kJ1DyLEGJJ9HEaP3s3jyFQgLXoi1J/8qyOESUDy
|
||||
ogC8nIod6vfA9g8eWcFeOEd6YnNWykPjGCqA/mzrzN3abFkERap8ivx4RWVYX9bP
|
||||
ZNhJVxcoqVCjtFmIh6ATNG/0+xfxax4U4GitcHNYD0Ij+qdXqNJHym67uVponLYE
|
||||
SnxxF3lZ6FzB51SyKtJF5j4/fEeyDXR8Uy3zJdrb6mhSUJNX5RYcrdiR9RflrfGy
|
||||
qYOXFtWmFv/+w/OaqugXQfx9By8uI49U9BG8Q7xSsZSXmoEuMGYt+UwE7UP+
|
||||
-----END CERTIFICATE-----
|
27
Tests/base/NSURLConnection/Helpers/testKey.pem
Normal file
27
Tests/base/NSURLConnection/Helpers/testKey.pem
Normal file
|
@ -0,0 +1,27 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpAIBAAKCAQEA8RVRB4dd+4cpdI7mL217gDlCF0UlcJPdpgBiSCJ9INA5uiiA
|
||||
CVtRBkeS3h2Ny765MRALxsW+2KarYNReDnMKGRP9WNMSoxwZ6rTCXMOw4DfRdXSJ
|
||||
EPR8R5GD4h80xXe35aKJrI8rdAXKgKhNUH9GobJ73qvROikyGEU2fdURh0KwFuCg
|
||||
cRTN9Inh2EGHZ9rw4B//MMi48EPvPT+UqwSXbfnihz4x747NVmXtIuutIby2tfZf
|
||||
w0OWcZVeSyerWco76/jo0/bRPOq/Z9+L5PmWQQkML82fnR8C6bn891YD2qeeGwgi
|
||||
2NtQWY9eZ6iEzZPvFyc/mmrxXt83TL0/XBomTQIDAQABAoIBAAZmlnwor+oZsJQT
|
||||
pzDjK0BAROzxPQk8I8pggDuCDuhsHtw+bwfQkNol1FRpXHZoXepbjrR8U5DU+//a
|
||||
I5UmoMIBsdxF3lzORjHhErf7yhpp4PnJWkpE83fC+Ulroq8LeqpyIk2ej3zJGpNH
|
||||
5KWae3mXj4pd7XQp29ahH80/dvOsWGZyYOXqh7jO6UdXfhPHKIhLN6wd9nzmRy44
|
||||
PLFcQYo/nTM19ackz9joovyGPy87BGsj3+rALRXd+GMPVtRFFPMtm6PnSTsMsoDg
|
||||
HEsm76CygnmL7/ywIXSVY/rfomm73SV9FgFalkUSpNWdLqiXiLICVTKC2P1ZUCdg
|
||||
eL/A59ECgYEA/nfw/9Vh86LVQ6Vqfp9W/pAut9ik8SecqodOptBSVV2YBYcfBejL
|
||||
dqksW3LaARabGMJh1z5tHfIw+buF7tl8OIy+TNYRNdrUlsNpI7lWVCVp5F4Znr3c
|
||||
a7GsGE82/XA3eiEZhUfaQeCctOMXoPnZO32mdDal3rYYQ7yKMvAO8gsCgYEA8ojA
|
||||
pK80FURe+kM0Zd/ftTAzMkGJNWnKkSSqd+sI7iCwzd9KAX3d+EM4/vqguMXzw+MP
|
||||
FMkXOv4PUP0iV1XyBt1hn+OSdfax4mD9ALOyJoCHvLPsRjlIv7iEsD2oi84mJ07v
|
||||
3fFuku+xDrPwMBJgeIWjC2ml9J3YYrB5ppPRmAcCgYEA+64/Y5l1ttW/XpeVm8UW
|
||||
8tJCEr2obYfDMPqAtQZn2FyohhcdfOfBjQxHfe87ZUYpgjSHNq9clvi6rdVl41Wh
|
||||
wgCaGz7CaOSVzMNbEuU1WCZk9GSJrHKWNsHUt3pppgK+LAHezu7BFNUFyPauoR1c
|
||||
WLWu01RVe8/Yce5hNX4vGf8CgYEA5w7pmPthfzFX2szTyopyMcftvl85PK3A0m5A
|
||||
CWbdZx+10Sx88Nbc9Xv1fNWA8QeFqIVVBNRfUVBhfyLp6JJ0tZ2LOCwyiDeyWJ1V
|
||||
66lGe+/PYTN4UZ6ZdC1yHAVh4W9QYfqOAr/UPCAman96wBGB3tBR+Ll55YXLdJn0
|
||||
C4KgF1kCgYAHjE0j82DF+6sKw+ggUcMgLM0z6HcYE975T/c4KNqb11Mu2+38CQvY
|
||||
pvlzOgQxzbjq59reek3gRtj3u0UCaXzTnIvgLl+sC0/l4f4qEVwcEcnMiJyUAIsR
|
||||
VR7Mw874clbF8TK8tLlcgCipmDmWxFlrgxOPs1ikcObvTOR/oxacUQ==
|
||||
-----END RSA PRIVATE KEY-----
|
101
Tests/base/NSURLConnection/Helpers/testTestWebServer.m
Normal file
101
Tests/base/NSURLConnection/Helpers/testTestWebServer.m
Normal file
|
@ -0,0 +1,101 @@
|
|||
/**
|
||||
*
|
||||
* Author: Sergei Golovin <Golovin.SV@gmail.com>
|
||||
*
|
||||
* Runs two TestWebServer instances to check how the class TestWebServer
|
||||
* behaves. Visit http://localhost:54321/index to see all supported resources.
|
||||
*
|
||||
* If you visit the main TestWebServer instance with the following command:
|
||||
*
|
||||
* wget -O - --user=login --password=password http://localhost:54321/301 2>&1
|
||||
*
|
||||
* you should get a session log like this:
|
||||
*
|
||||
* --2014-08-13 12:08:01-- http://localhost:54321/301
|
||||
* Resolving localhost (localhost)... 127.0.0.1
|
||||
* Connecting to localhost (localhost)|127.0.0.1|:54321... connected.
|
||||
* HTTP request sent, awaiting response... 401 Unauthorized
|
||||
* Reusing existing connection to localhost:54321.
|
||||
* HTTP request sent, awaiting response... 301 Moved Permanently
|
||||
* Location: http://127.0.0.1:54322/ [following]
|
||||
* --2014-08-13 12:08:01-- http://127.0.0.1:54322/
|
||||
* Connecting to 127.0.0.1:54322... connected.
|
||||
* HTTP request sent, awaiting response... 401 Unauthorized
|
||||
* Reusing existing connection to 127.0.0.1:54322.
|
||||
* HTTP request sent, awaiting response... 204 No Content
|
||||
* Length: 0
|
||||
* Saving to: ‘STDOUT’
|
||||
*
|
||||
* 0K 0.00 =0s
|
||||
*
|
||||
* 2014-08-13 12:08:01 (0.00 B/s) - written to stdout [0/0]
|
||||
*
|
||||
*/
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "TestWebServer.h"
|
||||
#import "NSURLConnectionTest.h"
|
||||
|
||||
#define TIMING 0.1
|
||||
|
||||
int main(int argc, char **argv, char **env)
|
||||
{
|
||||
CREATE_AUTORELEASE_POOL(arp);
|
||||
NSFileManager *fm;
|
||||
NSBundle *bundle;
|
||||
BOOL loaded;
|
||||
NSString *helperPath;
|
||||
|
||||
fm = [NSFileManager defaultManager];
|
||||
helperPath = [[fm currentDirectoryPath]
|
||||
stringByAppendingString: @"/TestConnection.bundle"];
|
||||
bundle = [NSBundle bundleWithPath: helperPath];
|
||||
loaded = [bundle load];
|
||||
|
||||
if(loaded)
|
||||
{
|
||||
TestWebServer *server1;
|
||||
TestWebServer *server2;
|
||||
Class testClass;
|
||||
BOOL debug = YES;
|
||||
NSDictionary *d;
|
||||
|
||||
testClass = [bundle principalClass]; // NSURLConnectionTest
|
||||
d = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
// @"https", @"Protocol",
|
||||
nil];
|
||||
server1 = [[[testClass testWebServerClass] alloc] initWithAddress: @"localhost"
|
||||
port: @"54321"
|
||||
mode: NO
|
||||
extra: d];
|
||||
[server1 setDebug: debug];
|
||||
[server1 start: d]; // 127.0.0.1:54321 HTTP
|
||||
|
||||
server2 = [[[testClass testWebServerClass] alloc] initWithAddress: @"localhost"
|
||||
port: @"54322"
|
||||
mode: NO
|
||||
extra: d];
|
||||
[server2 setDebug: debug];
|
||||
[server2 start: d]; // 127.0.0.1:54322 HTTP
|
||||
|
||||
while(YES)
|
||||
{
|
||||
[[NSRunLoop currentRunLoop]
|
||||
runUntilDate: [NSDate dateWithTimeIntervalSinceNow: TIMING]];
|
||||
}
|
||||
// [server1 stop];
|
||||
// DESTROY(server1);
|
||||
// [server2 stop];
|
||||
// DESTROY(server2);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
[NSException raise: NSInternalInconsistencyException
|
||||
format: @"can't load bundle TestConnection"];
|
||||
}
|
||||
|
||||
|
||||
DESTROY(arp);
|
||||
|
||||
return 0;
|
||||
}
|
137
Tests/base/NSURLConnection/test02.m
Normal file
137
Tests/base/NSURLConnection/test02.m
Normal file
|
@ -0,0 +1,137 @@
|
|||
/**
|
||||
* Tests for HTTP.
|
||||
*/
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "Helpers/NSURLConnectionTest.h"
|
||||
#import "Helpers/TestWebServer.h"
|
||||
#import <Testing.h>
|
||||
|
||||
int main(int argc, char **argv, char **env)
|
||||
{
|
||||
CREATE_AUTORELEASE_POOL(arp);
|
||||
NSFileManager *fm;
|
||||
NSBundle *bundle;
|
||||
BOOL loaded;
|
||||
NSString *helperPath;
|
||||
|
||||
// load the test suite's classes
|
||||
fm = [NSFileManager defaultManager];
|
||||
helperPath = [[fm currentDirectoryPath]
|
||||
stringByAppendingString: @"/Helpers/TestConnection.bundle"];
|
||||
bundle = [NSBundle bundleWithPath: helperPath];
|
||||
loaded = [bundle load];
|
||||
|
||||
if(loaded)
|
||||
{
|
||||
NSDictionary *d;
|
||||
Class testClass;
|
||||
NSDictionary *refs;
|
||||
TestWebServer *server;
|
||||
NSURLConnectionTest *testCase;
|
||||
BOOL debug = NO;
|
||||
|
||||
testClass = [bundle principalClass]; // NSURLConnectionTest
|
||||
|
||||
// create a shared TestWebServer instance for performance
|
||||
server = [[testClass testWebServerClass] new];
|
||||
[server setDebug: debug];
|
||||
[server start: nil]; // 127.0.0.1:54321 HTTP
|
||||
|
||||
/*
|
||||
* Simple GET via HTTP with empty response's body and
|
||||
* the response's status code 204 (by default)
|
||||
*/
|
||||
testCase = [testClass new];
|
||||
[testCase setDebug: debug];
|
||||
// the extra dictionary with test case's parameters
|
||||
d = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
server, @"Instance", // we use the shared TestWebServer instance
|
||||
nil];
|
||||
[testCase setUpTest: d];
|
||||
[testCase startTest: d];
|
||||
PASS([testCase isSuccess], "GET http://localhost:54321");
|
||||
[testCase tearDownTest: d];
|
||||
DESTROY(testCase);
|
||||
|
||||
/*
|
||||
* Simple GET via HTTP with the response's status code 400 and
|
||||
* non-empty response's body
|
||||
*/
|
||||
testCase = [testClass new];
|
||||
[testCase setDebug: debug];
|
||||
// the extra dictionary with test case's parameters
|
||||
d = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
server, @"Instance", // we use the shared TestWebServer instance
|
||||
@"400", @"Path", // request the handler responding with 400
|
||||
@"400", @"StatusCode", // the expected status code
|
||||
@"You have issued a request with invalid data", @"Content", // the expected response's body
|
||||
nil];
|
||||
[testCase setUpTest: d];
|
||||
[testCase startTest: d];
|
||||
PASS([testCase isSuccess], "response 400 .... GET http://localhost:54321/400");
|
||||
[testCase tearDownTest: d];
|
||||
DESTROY(testCase);
|
||||
|
||||
/*
|
||||
* Simple POST via HTTP with the response's status code 400 and
|
||||
* non-empty response's body
|
||||
*/
|
||||
testCase = [testClass new];
|
||||
[testCase setDebug: debug];
|
||||
// the extra dictionary with test case's parameters
|
||||
d = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
server, @"Instance", // we use the shared TestWebServer instance
|
||||
@"400", @"Path", // request the handler responding with 400
|
||||
@"400", @"StatusCode", // the expected status code
|
||||
@"You have issued a request with invalid data", @"Content", // the expected response's body
|
||||
@"Some payload", @"Payload", // the custom payload
|
||||
@"POST", @"Method", // use POST
|
||||
nil];
|
||||
[testCase setUpTest: d];
|
||||
[testCase startTest: d];
|
||||
PASS([testCase isSuccess], "payload... response 400 .... POST http://localhost:54321/400");
|
||||
[testCase tearDownTest: d];
|
||||
DESTROY(testCase);
|
||||
|
||||
/*
|
||||
* Tests redirecting... it uses an auxilliary TestWebServer instance and proceeds
|
||||
* in two stages. The first one is to get the status code 301 and go to the URL
|
||||
* given in the response's header 'Location'. The second stage is a simple GET on
|
||||
* the given URL with the status code 204 and empty response's body.
|
||||
*/
|
||||
testCase = [testClass new];
|
||||
[testCase setDebug: debug];
|
||||
// the reference set difference (from the default reference set) we expect
|
||||
refs = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
@"YES", @"GOTREDIRECT",
|
||||
nil];
|
||||
// the extra dictionary with test case's parameters
|
||||
d = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
server, @"Instance", // we use the shared TestWebServer instance
|
||||
@"/301", @"Path", // request the handler responding with a redirect
|
||||
@"/", @"RedirectPath", // the URL's path of redirecting
|
||||
@"YES", @"IsAuxilliary", // start an auxilliary TestWebServer instance
|
||||
refs, @"ReferenceFlags", // the expected reference set difference
|
||||
nil];
|
||||
[testCase setUpTest: d];
|
||||
[testCase startTest: d];
|
||||
PASS([testCase isSuccess], "redirecting... GET http://localhost:54321/301");
|
||||
[testCase tearDownTest: d];
|
||||
DESTROY(testCase);
|
||||
|
||||
// cleaning
|
||||
[server stop];
|
||||
DESTROY(server);
|
||||
}
|
||||
else
|
||||
{
|
||||
// no classes no tests
|
||||
[NSException raise: NSInternalInconsistencyException
|
||||
format: @"can't load bundle TestConnection"];
|
||||
}
|
||||
|
||||
|
||||
DESTROY(arp);
|
||||
|
||||
return 0;
|
||||
}
|
145
Tests/base/NSURLConnection/test03.m
Normal file
145
Tests/base/NSURLConnection/test03.m
Normal file
|
@ -0,0 +1,145 @@
|
|||
/**
|
||||
* Tests for HTTPS
|
||||
* (must be the same as test02.m except for the use of HTTPS).
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "Helpers/NSURLConnectionTest.h"
|
||||
#import "Helpers/TestWebServer.h"
|
||||
#import <Testing.h>
|
||||
|
||||
int main(int argc, char **argv, char **env)
|
||||
{
|
||||
CREATE_AUTORELEASE_POOL(arp);
|
||||
NSFileManager *fm;
|
||||
NSBundle *bundle;
|
||||
BOOL loaded;
|
||||
NSString *helperPath;
|
||||
|
||||
// load the test suite's classes
|
||||
fm = [NSFileManager defaultManager];
|
||||
helperPath = [[fm currentDirectoryPath]
|
||||
stringByAppendingString: @"/Helpers/TestConnection.bundle"];
|
||||
bundle = [NSBundle bundleWithPath: helperPath];
|
||||
loaded = [bundle load];
|
||||
|
||||
if(loaded)
|
||||
{
|
||||
NSDictionary *d;
|
||||
Class testClass;
|
||||
NSDictionary *refs;
|
||||
TestWebServer *server;
|
||||
NSURLConnectionTest *testCase;
|
||||
BOOL debug = NO;
|
||||
|
||||
testClass = [bundle principalClass]; // NSURLConnectionTest
|
||||
|
||||
// the extra dictionary commanding to use HTTPS
|
||||
d = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
@"HTTPS", @"Protocol",
|
||||
nil];
|
||||
// create a shared TestWebServer instance for performance
|
||||
server = [[[testClass testWebServerClass] alloc] initWithAddress: @"localhost"
|
||||
port: @"54321"
|
||||
mode: NO
|
||||
extra: d];
|
||||
[server setDebug: debug];
|
||||
[server start: d]; // 127.0.0.1:54321 HTTPS
|
||||
|
||||
/*
|
||||
* Simple GET via HTTPS with empty response's body and
|
||||
* the response's status code 204 (by default)
|
||||
*/
|
||||
testCase = [testClass new];
|
||||
[testCase setDebug: debug];
|
||||
// the extra dictionary with test case's parameters
|
||||
d = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
server, @"Instance", // we use the shared TestWebServer instance
|
||||
nil];
|
||||
[testCase setUpTest: d];
|
||||
[testCase startTest: d];
|
||||
PASS([testCase isSuccess], "HTTPS... GET https://localhost:54321");
|
||||
[testCase tearDownTest: d];
|
||||
DESTROY(testCase);
|
||||
|
||||
/*
|
||||
* Simple GET via HTTPS with the response's status code 400 and
|
||||
* non-empty response's body
|
||||
*/
|
||||
testCase = [testClass new];
|
||||
[testCase setDebug: debug];
|
||||
// the extra dictionary with test case's parameters
|
||||
d = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
server, @"Instance", // we use the shared TestWebServer instance
|
||||
@"400", @"Path", // request the handler responding with 400
|
||||
@"400", @"StatusCode", // the expected status code
|
||||
@"You have issued a request with invalid data", @"Content", // the expected response's body
|
||||
nil];
|
||||
[testCase setUpTest: d];
|
||||
[testCase startTest: d];
|
||||
PASS([testCase isSuccess], "HTTPS... response 400 .... GET https://localhost:54321/400");
|
||||
[testCase tearDownTest: d];
|
||||
DESTROY(testCase);
|
||||
|
||||
/*
|
||||
* Simple POST via HTTPS with the response's status code 400 and
|
||||
* non-empty response's body
|
||||
*/
|
||||
testCase = [testClass new];
|
||||
[testCase setDebug: debug];
|
||||
// the extra dictionary with test case's parameters
|
||||
d = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
server, @"Instance", // we use the shared TestWebServer instance
|
||||
@"400", @"Path", // request the handler responding with 400
|
||||
@"400", @"StatusCode", // the expected status code
|
||||
@"You have issued a request with invalid data", @"Content", // the expected response's body
|
||||
@"Some payload", @"Payload", // the custom payload
|
||||
@"POST", @"Method", // use POST
|
||||
nil];
|
||||
[testCase setUpTest: d];
|
||||
[testCase startTest: d];
|
||||
PASS([testCase isSuccess], "HTTPS... payload... response 400 .... POST https://localhost:54321/400");
|
||||
[testCase tearDownTest: d];
|
||||
DESTROY(testCase);
|
||||
|
||||
/*
|
||||
* Tests redirecting... it uses an auxilliary TestWebServer instance and proceeds
|
||||
* in two stages. The first one is to get the status code 301 and go to the URL
|
||||
* given in the response's header 'Location'. The second stage is a simple GET on
|
||||
* the given URL with the status code 204 and empty response's body.
|
||||
*/
|
||||
testCase = [testClass new];
|
||||
[testCase setDebug: debug];
|
||||
// the reference set difference (from the default reference set) we expect
|
||||
refs = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
@"YES", @"GOTREDIRECT",
|
||||
nil];
|
||||
// the extra dictionary with test case's parameters
|
||||
d = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
server, @"Instance", // we use the shared TestWebServer instance
|
||||
@"/301", @"Path", // request the handler responding with a redirect
|
||||
@"/", @"RedirectPath", // the URL's path of redirecting
|
||||
@"YES", @"IsAuxilliary", // start an auxilliary TestWebServer instance
|
||||
refs, @"ReferenceFlags", // the expected reference set difference
|
||||
nil];
|
||||
[testCase setUpTest: d];
|
||||
[testCase startTest: d];
|
||||
PASS([testCase isSuccess], "HTTPS... redirecting... GET https://localhost:54321/301");
|
||||
[testCase tearDownTest: d];
|
||||
DESTROY(testCase);
|
||||
|
||||
// cleaning
|
||||
[server stop];
|
||||
DESTROY(server);
|
||||
}
|
||||
else
|
||||
{
|
||||
// no classes no tests
|
||||
[NSException raise: NSInternalInconsistencyException
|
||||
format: @"can't load bundle TestConnection"];
|
||||
}
|
||||
|
||||
DESTROY(arp);
|
||||
|
||||
return 0;
|
||||
}
|
170
Tests/base/NSURLConnection/test04.m
Normal file
170
Tests/base/NSURLConnection/test04.m
Normal file
|
@ -0,0 +1,170 @@
|
|||
/**
|
||||
* Tests for HTTP without authorization.
|
||||
*/
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "Helpers/NSURLConnectionTest.h"
|
||||
#import "Helpers/TestWebServer.h"
|
||||
#import <Testing.h>
|
||||
|
||||
int main(int argc, char **argv, char **env)
|
||||
{
|
||||
CREATE_AUTORELEASE_POOL(arp);
|
||||
NSFileManager *fm;
|
||||
NSBundle *bundle;
|
||||
BOOL loaded;
|
||||
NSString *helperPath;
|
||||
|
||||
// load the test suite's classes
|
||||
fm = [NSFileManager defaultManager];
|
||||
helperPath = [[fm currentDirectoryPath]
|
||||
stringByAppendingString: @"/Helpers/TestConnection.bundle"];
|
||||
bundle = [NSBundle bundleWithPath: helperPath];
|
||||
loaded = [bundle load];
|
||||
|
||||
if(loaded)
|
||||
{
|
||||
NSDictionary *d;
|
||||
Class testClass;
|
||||
NSDictionary *refs;
|
||||
TestWebServer *server;
|
||||
NSURLConnectionTest *testCase;
|
||||
BOOL debug = NO;
|
||||
|
||||
testClass = [bundle principalClass]; // NSURLConnectionTest
|
||||
|
||||
// create a shared TestWebServer instance for performance
|
||||
server = [[testClass testWebServerClass] new];
|
||||
[server setDebug: debug];
|
||||
[server start: nil]; // 127.0.0.1:54321 HTTP
|
||||
|
||||
/*
|
||||
* Simple GET via HTTP without authorization with empty response's body and
|
||||
* the response's status code 204 (by default)
|
||||
*/
|
||||
testCase = [testClass new];
|
||||
[testCase setDebug: debug];
|
||||
// the reference set difference (from the default reference set) we expect
|
||||
// the flags must not be set because we request a path with no authorization
|
||||
// See NSURLConnectionTest.h for details (the key words are 'TestCase' and 'ReferenceFlags')
|
||||
refs = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
@"NO", @"GOTUNAUTHORIZED",
|
||||
@"NO", @"AUTHORIZED",
|
||||
@"NO", @"NOTAUTHORIZED",
|
||||
nil];
|
||||
// the extra dictionary with test case's parameters
|
||||
d = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
server, @"Instance", // we use the shared TestWebServer instance
|
||||
@"/withoutauth", @"Path", // the path commands to use no authorization
|
||||
refs, @"ReferenceFlags", // the expected reference set difference
|
||||
nil];
|
||||
[testCase setUpTest: d];
|
||||
[testCase startTest: d];
|
||||
PASS([testCase isSuccess], "no auth... GET http://localhost:54321/withoutauth");
|
||||
[testCase tearDownTest: d];
|
||||
DESTROY(testCase);
|
||||
|
||||
/*
|
||||
* Simple GET via HTTP without authorization with the response's status code 400
|
||||
* and non-empty response's body
|
||||
*/
|
||||
testCase = [testClass new];
|
||||
[testCase setDebug: debug];
|
||||
// the reference set difference (from the default reference set) we expect
|
||||
// the flags must not be set because we request a path with no authorization
|
||||
// See NSURLConnectionTest.h for details (the key words are 'TestCase' and 'ReferenceFlags')
|
||||
refs = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
@"NO", @"GOTUNAUTHORIZED",
|
||||
@"NO", @"AUTHORIZED",
|
||||
@"NO", @"NOTAUTHORIZED",
|
||||
nil];
|
||||
// the extra dictionary with test case's parameters
|
||||
d = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
server, @"Instance", // we use the shared TestWebServer instance
|
||||
@"400/withoutauth", @"Path", // request the handler responding with 400
|
||||
@"400", @"StatusCode", // the expected status code
|
||||
@"You have issued a request with invalid data", @"Content", // the expected response's body
|
||||
refs, @"ReferenceFlags", // the expected reference set difference
|
||||
nil];
|
||||
[testCase setUpTest: d];
|
||||
[testCase startTest: d];
|
||||
PASS([testCase isSuccess], "no auth... response 400 .... GET http://localhost:54321/400/withoutauth");
|
||||
[testCase tearDownTest: d];
|
||||
DESTROY(testCase);
|
||||
|
||||
/*
|
||||
* Simple POST via HTTP with the response's status code 400 and
|
||||
* non-empty response's body
|
||||
*/
|
||||
testCase = [testClass new];
|
||||
[testCase setDebug: debug];
|
||||
// the reference set difference (from the default reference set) we expect
|
||||
// the flags must not be set because we request a path with no authorization
|
||||
// See NSURLConnectionTest.h for details (the key words are 'TestCase' and 'ReferenceFlags')
|
||||
refs = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
@"NO", @"GOTUNAUTHORIZED",
|
||||
@"NO", @"AUTHORIZED",
|
||||
@"NO", @"NOTAUTHORIZED",
|
||||
nil];
|
||||
// the extra dictionary with test case's parameters
|
||||
d = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
server, @"Instance", // we use the shared TestWebServer instance
|
||||
@"400/withoutauth", @"Path", // request the handler responding with 400
|
||||
@"400", @"StatusCode", // the expected status code
|
||||
@"You have issued a request with invalid data", @"Content", // the expected response's body
|
||||
@"Some payload", @"Payload", // the custom payload
|
||||
@"POST", @"Method", // use POST
|
||||
refs, @"ReferenceFlags", // the expected reference set difference
|
||||
nil];
|
||||
[testCase setUpTest: d];
|
||||
[testCase startTest: d];
|
||||
PASS([testCase isSuccess], "no auth... payload... response 400 .... POST http://localhost:54321/400/withoutauth");
|
||||
[testCase tearDownTest: d];
|
||||
DESTROY(testCase);
|
||||
|
||||
/*
|
||||
* Tests redirecting... it uses an auxilliary TestWebServer instance and proceeds
|
||||
* in two stages. The first one is to get the status code 301 and go to the URL
|
||||
* given in the response's header 'Location'. The second stage is a simple GET on
|
||||
* the given URL with the status code 204 and empty response's body.
|
||||
*/
|
||||
testCase = [testClass new];
|
||||
[testCase setDebug: debug];
|
||||
// the reference set difference (from the default reference set) we expect
|
||||
// the flags must not be set because we request a path with no authorization
|
||||
// See NSURLConnectionTest.h for details (the key words are 'TestCase' and 'ReferenceFlags')
|
||||
refs = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
@"NO", @"GOTUNAUTHORIZED",
|
||||
@"NO", @"AUTHORIZED",
|
||||
@"NO", @"NOTAUTHORIZED",
|
||||
@"YES", @"GOTREDIRECT",
|
||||
nil];
|
||||
// the extra dictionary with test case's parameters
|
||||
d = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
server, @"Instance", // we use the shared TestWebServer instance
|
||||
@"/301/withoutauth", @"Path", // request the handler responding with a redirect
|
||||
@"/withoutauth", @"RedirectPath", // the URL's path of redirecting
|
||||
@"YES", @"IsAuxilliary", // start an auxilliary TestWebServer instance
|
||||
refs, @"ReferenceFlags", // the expected reference set difference
|
||||
nil];
|
||||
[testCase setUpTest: d];
|
||||
[testCase startTest: d];
|
||||
PASS([testCase isSuccess], "no auth... redirecting... GET http://localhost:54321/301/withoutauth");
|
||||
[testCase tearDownTest: d];
|
||||
DESTROY(testCase);
|
||||
|
||||
// cleaning
|
||||
[server stop];
|
||||
DESTROY(server);
|
||||
}
|
||||
else
|
||||
{
|
||||
// no classes no tests
|
||||
[NSException raise: NSInternalInconsistencyException
|
||||
format: @"can't load bundle TestConnection"];
|
||||
}
|
||||
|
||||
|
||||
DESTROY(arp);
|
||||
|
||||
return 0;
|
||||
}
|
178
Tests/base/NSURLConnection/test05.m
Normal file
178
Tests/base/NSURLConnection/test05.m
Normal file
|
@ -0,0 +1,178 @@
|
|||
/**
|
||||
* Tests for HTTPS without authorization.
|
||||
* (must be the same as test04.m except for the use of HTTPS).
|
||||
*/
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "Helpers/NSURLConnectionTest.h"
|
||||
#import "Helpers/TestWebServer.h"
|
||||
#import <Testing.h>
|
||||
|
||||
int main(int argc, char **argv, char **env)
|
||||
{
|
||||
CREATE_AUTORELEASE_POOL(arp);
|
||||
NSFileManager *fm;
|
||||
NSBundle *bundle;
|
||||
BOOL loaded;
|
||||
NSString *helperPath;
|
||||
|
||||
// load the test suite's classes
|
||||
fm = [NSFileManager defaultManager];
|
||||
helperPath = [[fm currentDirectoryPath]
|
||||
stringByAppendingString: @"/Helpers/TestConnection.bundle"];
|
||||
bundle = [NSBundle bundleWithPath: helperPath];
|
||||
loaded = [bundle load];
|
||||
|
||||
if(loaded)
|
||||
{
|
||||
NSDictionary *d;
|
||||
Class testClass;
|
||||
NSDictionary *refs;
|
||||
TestWebServer *server;
|
||||
NSURLConnectionTest *testCase;
|
||||
BOOL debug = NO;
|
||||
|
||||
testClass = [bundle principalClass]; // NSURLConnectionTest
|
||||
|
||||
// the extra dictionary commanding to use HTTPS
|
||||
d = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
@"HTTPS", @"Protocol",
|
||||
nil];
|
||||
// create a shared TestWebServer instance for performance
|
||||
server = [[[testClass testWebServerClass] alloc] initWithAddress: @"localhost"
|
||||
port: @"54321"
|
||||
mode: NO
|
||||
extra: d];
|
||||
[server setDebug: debug];
|
||||
[server start: d]; // 127.0.0.1:54321 HTTPS
|
||||
|
||||
/*
|
||||
* Simple GET via HTTPS without authorization with empty response's body and
|
||||
* the response's status code 204 (by default)
|
||||
*/
|
||||
testCase = [testClass new];
|
||||
[testCase setDebug: debug];
|
||||
// the reference set difference (from the default reference set) we expect
|
||||
// the flags must not be set because we request a path with no authorization
|
||||
// See NSURLConnectionTest.h for details (the key words are 'TestCase' and 'ReferenceFlags')
|
||||
refs = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
@"NO", @"GOTUNAUTHORIZED",
|
||||
@"NO", @"AUTHORIZED",
|
||||
@"NO", @"NOTAUTHORIZED",
|
||||
nil];
|
||||
// the extra dictionary with test case's parameters
|
||||
d = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
server, @"Instance", // we use the shared TestWebServer instance
|
||||
@"/withoutauth", @"Path", // the path commands to use no authorization
|
||||
refs, @"ReferenceFlags", // the expected reference set difference
|
||||
nil];
|
||||
[testCase setUpTest: d];
|
||||
[testCase startTest: d];
|
||||
PASS([testCase isSuccess], "HTTPS... no auth...GET https://localhost:54321/withoutauth");
|
||||
[testCase tearDownTest: d];
|
||||
DESTROY(testCase);
|
||||
|
||||
/*
|
||||
* Simple GET via HTTPS without authorization with the response's status code 400
|
||||
* and non-empty response's body
|
||||
*/
|
||||
testCase = [testClass new];
|
||||
[testCase setDebug: debug];
|
||||
// the reference set difference (from the default reference set) we expect
|
||||
// the flags must not be set because we request a path with no authorization
|
||||
// See NSURLConnectionTest.h for details (the key words are 'TestCase' and 'ReferenceFlags')
|
||||
refs = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
@"NO", @"GOTUNAUTHORIZED",
|
||||
@"NO", @"AUTHORIZED",
|
||||
@"NO", @"NOTAUTHORIZED",
|
||||
nil];
|
||||
// the extra dictionary with test case's parameters
|
||||
d = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
server, @"Instance", // we use the shared TestWebServer instance
|
||||
@"400/withoutauth", @"Path", // request the handler responding with 400
|
||||
@"400", @"StatusCode", // the expected status code
|
||||
@"You have issued a request with invalid data", @"Content", // the expected response's body
|
||||
refs, @"ReferenceFlags", // the expected reference set difference
|
||||
nil];
|
||||
[testCase setUpTest: d];
|
||||
[testCase startTest: d];
|
||||
PASS([testCase isSuccess], "HTTPS... no auth... response 400... GET https://localhost:54321/400/withoutauth");
|
||||
[testCase tearDownTest: d];
|
||||
DESTROY(testCase);
|
||||
|
||||
/*
|
||||
* Simple POST via HTTPS with the response's status code 400 and
|
||||
* non-empty response's body
|
||||
*/
|
||||
testCase = [testClass new];
|
||||
[testCase setDebug: debug];
|
||||
// the reference set difference (from the default reference set) we expect
|
||||
// the flags must not be set because we request a path with no authorization
|
||||
// See NSURLConnectionTest.h for details (the key words are 'TestCase' and 'ReferenceFlags')
|
||||
refs = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
@"NO", @"GOTUNAUTHORIZED",
|
||||
@"NO", @"AUTHORIZED",
|
||||
@"NO", @"NOTAUTHORIZED",
|
||||
nil];
|
||||
// the extra dictionary with test case's parameters
|
||||
d = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
server, @"Instance", // we use the shared TestWebServer instance
|
||||
@"400/withoutauth", @"Path", // request the handler responding with 400
|
||||
@"400", @"StatusCode", // the expected status code
|
||||
@"You have issued a request with invalid data", @"Content", // the expected response's body
|
||||
@"Some payload", @"Payload", // the custom payload
|
||||
@"POST", @"Method", // use POST
|
||||
refs, @"ReferenceFlags", // the expected reference set difference
|
||||
nil];
|
||||
[testCase setUpTest: d];
|
||||
[testCase startTest: d];
|
||||
PASS([testCase isSuccess], "HTTPS... no auth... payload... response 400 .... POST https://localhost:54321/400/withoutauth");
|
||||
[testCase tearDownTest: d];
|
||||
DESTROY(testCase);
|
||||
|
||||
/*
|
||||
* Tests redirecting... it uses an auxilliary TestWebServer instance and proceeds
|
||||
* in two stages. The first one is to get the status code 301 and go to the URL
|
||||
* given in the response's header 'Location'. The second stage is a simple GET on
|
||||
* the given URL with the status code 204 and empty response's body.
|
||||
*/
|
||||
testCase = [testClass new];
|
||||
[testCase setDebug: debug];
|
||||
// the reference set difference (from the default reference set) we expect
|
||||
// the flags must not be set because we request a path with no authorization
|
||||
// See NSURLConnectionTest.h for details (the key words are 'TestCase' and 'ReferenceFlags')
|
||||
refs = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
@"NO", @"GOTUNAUTHORIZED",
|
||||
@"NO", @"AUTHORIZED",
|
||||
@"NO", @"NOTAUTHORIZED",
|
||||
@"YES", @"GOTREDIRECT",
|
||||
nil];
|
||||
// the extra dictionary with test case's parameters
|
||||
d = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
server, @"Instance", // we use the shared TestWebServer instance
|
||||
@"/301/withoutauth", @"Path", // request the handler responding with a redirect
|
||||
@"/withoutauth", @"RedirectPath", // the URL's path of redirecting
|
||||
@"YES", @"IsAuxilliary", // start an auxilliary TestWebServer instance
|
||||
refs, @"ReferenceFlags", // the expected reference set difference
|
||||
nil];
|
||||
[testCase setUpTest: d];
|
||||
[testCase startTest: d];
|
||||
PASS([testCase isSuccess], "HTTPS... no auth... redirecting... GET https://localhost:54321/301/withoutauth");
|
||||
[testCase tearDownTest: d];
|
||||
DESTROY(testCase);
|
||||
|
||||
// cleaning
|
||||
[server stop];
|
||||
DESTROY(server);
|
||||
}
|
||||
else
|
||||
{
|
||||
// no classes no tests
|
||||
[NSException raise: NSInternalInconsistencyException
|
||||
format: @"can't load bundle TestConnection"];
|
||||
}
|
||||
|
||||
|
||||
DESTROY(arp);
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Reference in a new issue