Index: new_tests/Makefile ================================================================== --- new_tests/Makefile +++ new_tests/Makefile @@ -54,11 +54,12 @@ ${USE_SRCS_THREADS} \ ${USE_SRCS_WINDOWS} \ testfile_bin.m \ testfile_ini.m SRCS_PLUGINS = OFPluginTests.m -SRCS_SOCKETS = OFHTTPCookieManagerTests.m \ +SRCS_SOCKETS = ${OF_HTTP_CLIENT_TESTS_M} \ + OFHTTPCookieManagerTests.m \ OFHTTPCookieTests.m \ OFDNSResolverTests.m \ OFSocketTests.m \ OFTCPSocketTests.m \ OFUDPSocketTests.m \ ADDED new_tests/OFHTTPClientTests.m Index: new_tests/OFHTTPClientTests.m ================================================================== --- new_tests/OFHTTPClientTests.m +++ new_tests/OFHTTPClientTests.m @@ -0,0 +1,200 @@ +/* + * Copyright (c) 2008-2024 Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +#include +#include + +#import "ObjFW.h" +#import "ObjFWTest.h" + +@interface OFHTTPClientTests: OTTestCase +{ + OFHTTPResponse *_response; +} +@end + +@interface HTTPClientTestsServer: OFThread +{ + OFCondition *_condition; + uint16_t _port; +} + +@property (readonly, nonatomic) OFCondition *condition; +@property (readonly) uint16_t port; +@end + +@implementation OFHTTPClientTests +- (void)dealloc +{ + [_response release]; + + [super dealloc]; +} + +- (void)client: (OFHTTPClient *)client + wantsRequestBody: (OFStream *)body + request: (OFHTTPRequest *)request +{ + [body writeString: @"Hello"]; +} + +- (void)client: (OFHTTPClient *)client + didPerformRequest: (OFHTTPRequest *)request + response: (OFHTTPResponse *)response_ + exception: (id)exception +{ + OTAssertNil(exception); + + [_response release]; + _response = [response_ retain]; + + [[OFRunLoop mainRunLoop] stop]; +} + +- (void)testClient +{ + HTTPClientTestsServer *server; + OFIRI *IRI; + OFHTTPRequest *request; + OFHTTPClient *client; + OFData *data; + + server = [[[HTTPClientTestsServer alloc] init] autorelease]; + server.supportsSockets = true; + + [server.condition lock]; + + [server start]; + + [server.condition wait]; + [server.condition unlock]; + + IRI = [OFIRI IRIWithString: + [OFString stringWithFormat: @"http://127.0.0.1:%" @PRIu16 "/foo", + server.port]]; + + request = [OFHTTPRequest requestWithIRI: IRI]; + request.headers = [OFDictionary + dictionaryWithObject: @"5" + forKey: @"Content-Length"]; + + client = [OFHTTPClient client]; + client.delegate = self; + [client asyncPerformRequest: request]; + + [[OFRunLoop mainRunLoop] runUntilDate: + [OFDate dateWithTimeIntervalSinceNow: 2]]; + + OTAssertNotNil(_response); + OTAssertNotNil([_response.headers objectForKey: @"Content-Length"]); + + data = [_response readDataUntilEndOfStream]; + OTAssertEqual(data.count, 7); + OTAssertEqual(data.itemSize, 1); + OTAssertEqual(memcmp(data.items, "foo\nbar", 7), 0); + + OTAssertNil([server join]); +} +@end + +@implementation HTTPClientTestsServer +@synthesize condition = _condition, port = _port; + +- (instancetype)init +{ + self = [super init]; + + @try { + _condition = [[OFCondition alloc] init]; + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- (void)dealloc +{ + [_condition release]; + + [super dealloc]; +} + +- (id)main +{ + OFTCPSocket *listener, *client; + OFSocketAddress address; + bool sawHost = false, sawContentLength = false, sawContentType = false; + bool sawUserAgent = false; + char buffer[5]; + + [_condition lock]; + + listener = [OFTCPSocket socket]; + address = [listener bindToHost: @"127.0.0.1" port: 0]; + _port = OFSocketAddressIPPort(&address); + [listener listen]; + + [_condition signal]; + [_condition unlock]; + client = [listener accept]; + + if (![[client readLine] isEqual: @"GET /foo HTTP/1.1"]) + return @"Wrong request"; + + for (size_t i = 0; i < 4; i++) { + OFString *line = [client readLine]; + + if ([line isEqual: [OFString stringWithFormat: + @"Host: 127.0.0.1:%" @PRIu16, _port]]) + sawHost = true; + else if ([line isEqual: @"Content-Length: 5"]) + sawContentLength = true; + if ([line isEqual: @"Content-Type: application/" + @"x-www-form-urlencoded; charset=UTF-8"]) + sawContentType = true; + else if ([line hasPrefix: @"User-Agent:"]) + sawUserAgent = true; + } + + if (!sawHost) + return @"Missing host"; + if (!sawContentLength) + return @"Missing content length"; + if (!sawContentType) + return @"Missing content type"; + if (!sawUserAgent) + return @"Missing user agent"; + + if (![[client readLine] isEqual: @""]) + return @"Missing empty line"; + + [client readIntoBuffer: buffer exactLength: 5]; + if (memcmp(buffer, "Hello", 5) != 0) + return @"Missing body"; + + [client writeString: @"HTTP/1.0 200 OK\r\n" + @"cONTeNT-lENgTH: 7\r\n" + @"\r\n" + @"foo\n" + @"bar"]; + [client close]; + + return nil; +} +@end Index: tests/Makefile ================================================================== --- tests/Makefile +++ tests/Makefile @@ -22,12 +22,11 @@ OFXMLNodeTests.m \ OFXMLParserTests.m \ TestsAppDelegate.m \ ${USE_SRCS_FILES} \ ${USE_SRCS_SOCKETS} -SRCS_SOCKETS = ${OF_HTTP_CLIENT_TESTS_M} \ - OFKernelEventObserverTests.m +SRCS_SOCKETS = OFKernelEventObserverTests.m IOS_USER ?= mobile IOS_TMP ?= /tmp/objfw-test include ../buildsys.mk DELETED tests/OFHTTPClientTests.m Index: tests/OFHTTPClientTests.m ================================================================== --- tests/OFHTTPClientTests.m +++ tests/OFHTTPClientTests.m @@ -1,150 +0,0 @@ -/* - * Copyright (c) 2008-2024 Jonathan Schleifer - * - * All rights reserved. - * - * This file is part of ObjFW. It may be distributed under the terms of the - * Q Public License 1.0, which can be found in the file LICENSE.QPL included in - * the packaging of this file. - * - * Alternatively, it may be distributed under the terms of the GNU General - * Public License, either version 2 or 3, which can be found in the file - * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this - * file. - */ - -#include "config.h" - -#include -#include - -#import "TestsAppDelegate.h" - -static OFString *const module = @"OFHTTPClient"; -static OFCondition *condition; -static OFHTTPResponse *response = nil; - -@interface TestsAppDelegate (HTTPClientTests) -@end - -@interface HTTPClientTestsServer: OFThread -{ -@public - uint16_t _port; -} -@end - -@implementation HTTPClientTestsServer -- (id)main -{ - OFTCPSocket *listener, *client; - OFSocketAddress address; - char buffer[5]; - - [condition lock]; - - listener = [OFTCPSocket socket]; - address = [listener bindToHost: @"127.0.0.1" port: 0]; - _port = OFSocketAddressIPPort(&address); - [listener listen]; - - [condition signal]; - [condition unlock]; - - client = [listener accept]; - - OFEnsure([[client readLine] isEqual: @"GET /foo HTTP/1.1"]); - OFEnsure([[client readLine] hasPrefix: @"User-Agent:"]); - OFEnsure([[client readLine] isEqual: @"Content-Length: 5"]); - OFEnsure([[client readLine] isEqual: - @"Content-Type: application/x-www-form-urlencoded; charset=UTF-8"]); - - if (![[client readLine] isEqual: - [OFString stringWithFormat: @"Host: 127.0.0.1:%" @PRIu16, _port]]) - OFEnsure(0); - - OFEnsure([[client readLine] isEqual: @""]); - - [client readIntoBuffer: buffer exactLength: 5]; - OFEnsure(memcmp(buffer, "Hello", 5) == 0); - - [client writeString: @"HTTP/1.0 200 OK\r\n" - @"cONTeNT-lENgTH: 7\r\n" - @"\r\n" - @"foo\n" - @"bar"]; - [client close]; - - return nil; -} -@end - -@implementation TestsAppDelegate (OFHTTPClientTests) -- (void)client: (OFHTTPClient *)client - wantsRequestBody: (OFStream *)body - request: (OFHTTPRequest *)request -{ - [body writeString: @"Hello"]; -} - -- (void)client: (OFHTTPClient *)client - didPerformRequest: (OFHTTPRequest *)request - response: (OFHTTPResponse *)response_ - exception: (id)exception -{ - OFEnsure(exception == nil); - - response = [response_ retain]; - - [[OFRunLoop mainRunLoop] stop]; -} - -- (void)HTTPClientTests -{ - void *pool = objc_autoreleasePoolPush(); - HTTPClientTestsServer *server; - OFIRI *IRI; - OFHTTPClient *client; - OFHTTPRequest *request; - OFData *data; - - condition = [OFCondition condition]; - [condition lock]; - - server = [[[HTTPClientTestsServer alloc] init] autorelease]; - server.supportsSockets = true; - [server start]; - - [condition wait]; - [condition unlock]; - - IRI = [OFIRI IRIWithString: - [OFString stringWithFormat: @"http://127.0.0.1:%" @PRIu16 "/foo", - server->_port]]; - - TEST(@"-[asyncPerformRequest:]", - (client = [OFHTTPClient client]) && (client.delegate = self) && - (request = [OFHTTPRequest requestWithIRI: IRI]) && - (request.headers = - [OFDictionary dictionaryWithObject: @"5" - forKey: @"Content-Length"]) && - R([client asyncPerformRequest: request])) - - [[OFRunLoop mainRunLoop] runUntilDate: - [OFDate dateWithTimeIntervalSinceNow: 2]]; - [response autorelease]; - - TEST(@"Asynchronous handling of requests", response != nil) - - TEST(@"Normalization of server header keys", - [response.headers objectForKey: @"Content-Length"] != nil) - - TEST(@"Correct parsing of data", - (data = [response readDataUntilEndOfStream]) && - data.count == 7 && memcmp(data.items, "foo\nbar", 7) == 0) - - [server join]; - - objc_autoreleasePoolPop(pool); -} -@end Index: tests/TestsAppDelegate.h ================================================================== --- tests/TestsAppDelegate.h +++ tests/TestsAppDelegate.h @@ -65,14 +65,10 @@ @interface TestsAppDelegate (OFDictionaryTests) - (void)dictionaryTests; @end -@interface TestsAppDelegate (OFHTTPClientTests) -- (void)HTTPClientTests; -@end - @interface TestsAppDelegate (OFKernelEventObserverTests) - (void)kernelEventObserverTests; @end @interface TestsAppDelegate (OFListTests) Index: tests/TestsAppDelegate.m ================================================================== --- tests/TestsAppDelegate.m +++ tests/TestsAppDelegate.m @@ -377,13 +377,10 @@ [self streamTests]; [self memoryStreamTests]; #ifdef OF_HAVE_SOCKETS [self kernelEventObserverTests]; #endif -#if defined(OF_HAVE_SOCKETS) && defined(OF_HAVE_THREADS) - [self HTTPClientTests]; -#endif [self XMLParserTests]; [self XMLNodeTests]; [OFStdOut reset];