Index: configure.ac ================================================================== --- configure.ac +++ configure.ac @@ -1397,16 +1397,14 @@ AC_MSG_RESULT($have_threadsafe_getaddrinfo) ]) ], [ AC_MSG_RESULT(no) ]) -]) -AS_IF([test x"$enable_sockets" != x"no" -a x"$enable_threads" != x"no"], [ - AC_SUBST(OFHTTPCLIENT_M, "OFHTTPClient.m") - AC_SUBST(OFHTTPCLIENTTESTS_M, "OFHTTPClientTests.m") - AC_SUBST(OFURLHANDLER_HTTP_M, "OFURLHandler_HTTP.m") + AS_IF([test x"$enable_threads" != x"no"], [ + AC_SUBST(OFHTTPCLIENTTESTS_M, "OFHTTPClientTests.m") + ]) AC_SUBST(OFDNS, "ofdns") AS_IF([test x"$enable_files" != x"no"], [ AC_SUBST(OFHTTP, "ofhttp") ]) Index: extra.mk.in ================================================================== --- extra.mk.in +++ extra.mk.in @@ -55,18 +55,16 @@ OFBLOCKTESTS_M = @OFBLOCKTESTS_M@ OFDNS = @OFDNS@ OFHASH = @OFHASH@ OFHTTP = @OFHTTP@ OFHTTPCLIENTTESTS_M = @OFHTTPCLIENTTESTS_M@ -OFHTTPCLIENT_M = @OFHTTPCLIENT_M@ OFKERNELEVENTOBSERVER_EPOLL_M = @OFKERNELEVENTOBSERVER_EPOLL_M@ OFKERNELEVENTOBSERVER_KQUEUE_M = @OFKERNELEVENTOBSERVER_KQUEUE_M@ OFKERNELEVENTOBSERVER_POLL_M = @OFKERNELEVENTOBSERVER_POLL_M@ OFKERNELEVENTOBSERVER_SELECT_M = @OFKERNELEVENTOBSERVER_SELECT_M@ OFPROCESS_M = @OFPROCESS_M@ OFSTDIOSTREAM_WIN32CONSOLE_M = @OFSTDIOSTREAM_WIN32CONSOLE_M@ -OFURLHANDLER_HTTP_M = @OFURLHANDLER_HTTP_M@ REEXPORT_RUNTIME = @REEXPORT_RUNTIME@ REEXPORT_RUNTIME_FRAMEWORK = @REEXPORT_RUNTIME_FRAMEWORK@ RUNTIME = @RUNTIME@ RUNTIME_FRAMEWORK_LIBS = @RUNTIME_FRAMEWORK_LIBS@ RUNTIME_LIBS = @RUNTIME_LIBS@ Index: src/Makefile ================================================================== --- src/Makefile +++ src/Makefile @@ -123,11 +123,11 @@ OFSettings.m \ OFString+PathAdditions.m SRCS_PLUGINS = OFPlugin.m SRCS_SOCKETS = OFDNSResolver.m \ OFDNSResourceRecord.m \ - ${OFHTTPCLIENT_M} \ + OFHTTPClient.m \ OFHTTPCookie.m \ OFHTTPCookieManager.m \ OFHTTPRequest.m \ OFHTTPResponse.m \ OFHTTPServer.m \ @@ -183,11 +183,10 @@ OFMutableDictionary_hashtable.m \ OFMutableSet_hashtable.m \ OFMutableString_UTF8.m \ OFSet_hashtable.m \ OFString_UTF8.m \ - ${OFURLHANDLER_HTTP_M} \ OFValue_bytes.m \ OFValue_dimension.m \ OFValue_nonretainedObject.m \ OFValue_point.m \ OFValue_pointer.m \ @@ -201,11 +200,12 @@ SRCS_SOCKETS += OFKernelEventObserver.m \ ${OFKERNELEVENTOBSERVER_EPOLL_M} \ ${OFKERNELEVENTOBSERVER_KQUEUE_M} \ ${OFKERNELEVENTOBSERVER_POLL_M} \ ${OFKERNELEVENTOBSERVER_SELECT_M} \ - OFTCPSocket+SOCKS5.m + OFTCPSocket+SOCKS5.m \ + OFURLHandler_HTTP.m OBJS_EXTRA = ${RUNTIME_RUNTIME_A} \ ${EXCEPTIONS_EXCEPTIONS_A} \ ${ENCODINGS_ENCODINGS_A} \ ${FORWARDING_FORWARDING_A} \ Index: src/OFTCPSocket.h ================================================================== --- src/OFTCPSocket.h +++ src/OFTCPSocket.h @@ -151,11 +151,10 @@ * @param port The port on the host to connect to */ - (void)connectToHost: (OFString *)host port: (uint16_t)port; -#ifdef OF_HAVE_THREADS /*! * @brief Asynchronously connect the OFTCPSocket to the specified destination. * * @param host The host to connect to * @param port The port on the host to connect to @@ -169,11 +168,11 @@ port: (uint16_t)port target: (id)target selector: (SEL)selector context: (nullable id)context; -# ifdef OF_HAVE_BLOCKS +#ifdef OF_HAVE_BLOCKS /*! * @brief Asynchronously connect the OFTCPSocket to the specified destination. * * @param host The host to connect to * @param port The port on the host to connect to @@ -180,11 +179,10 @@ * @param block The block to execute once the connection has been established */ - (void)asyncConnectToHost: (OFString *)host port: (uint16_t)port block: (of_tcp_socket_async_connect_block_t)block; -# endif #endif /*! * @brief Bind the socket to the specified host and port. * Index: src/OFTCPSocket.m ================================================================== --- src/OFTCPSocket.m +++ src/OFTCPSocket.m @@ -31,22 +31,25 @@ #endif #import "OFTCPSocket.h" #import "OFTCPSocket+Private.h" #import "OFTCPSocket+SOCKS5.h" +#import "OFDNSResolver.h" +#import "OFData.h" +#import "OFRunLoop.h" +#import "OFRunLoop+Private.h" #import "OFString.h" #import "OFThread.h" #import "OFTimer.h" -#import "OFRunLoop.h" -#import "OFRunLoop+Private.h" #import "OFAcceptFailedException.h" #import "OFAlreadyConnectedException.h" #import "OFBindFailedException.h" #import "OFConnectionFailedException.h" #import "OFGetOptionFailedException.h" #import "OFInvalidArgumentException.h" +#import "OFInvalidFormatException.h" #import "OFListenFailedException.h" #import "OFNotImplementedException.h" #import "OFNotOpenException.h" #import "OFOutOfMemoryException.h" #import "OFOutOfRangeException.h" @@ -66,56 +69,62 @@ Class of_tls_socket_class = Nil; static OFString *defaultSOCKS5Host = nil; static uint16_t defaultSOCKS5Port = 1080; -#ifdef OF_HAVE_THREADS -@interface OFTCPSocket_ConnectThread: OFThread +@interface OFTCPSocket_ConnectContext: OFObject { - OFThread *_sourceThread; OFTCPSocket *_socket; OFString *_host; uint16_t _port; id _target; SEL _selector; id _context; -# ifdef OF_HAVE_BLOCKS +#ifdef OF_HAVE_BLOCKS of_tcp_socket_async_connect_block_t _block; -# endif +#endif id _exception; -} - -- (instancetype)initWithSourceThread: (OFThread *)sourceThread - socket: (OFTCPSocket *)sock - host: (OFString *)host - port: (uint16_t)port - target: (id)target - selector: (SEL)selector - context: (id)context; -# ifdef OF_HAVE_BLOCKS -- (instancetype)initWithSourceThread: (OFThread *)sourceThread - socket: (OFTCPSocket *)sock - host: (OFString *)host - port: (uint16_t)port - block: (of_tcp_socket_async_connect_block_t) - block; -# endif + OFData *_socketAddresses; + size_t _socketAddressesIndex; +} + +- (instancetype)initWithSocket: (OFTCPSocket *)sock + host: (OFString *)host + port: (uint16_t)port + target: (id)target + selector: (SEL)selector + context: (id)context; +#ifdef OF_HAVE_BLOCKS +- (instancetype)initWithSocket: (OFTCPSocket *)sock + host: (OFString *)host + port: (uint16_t)port + block: (of_tcp_socket_async_connect_block_t)block; +#endif +- (void)didConnect; +- (void)socketDidConnect: (OFTCPSocket *)sock + context: (id)context + exception: (id)exception; +- (void)tryNextAddress; +- (void)resolver: (OFDNSResolver *)resolver + didResolveDomainName: (OFString *)domainName + socketAddresses: (OFData *)socketAddresses + context: (id)context + exception: (id)exception; +- (void)start; @end -@implementation OFTCPSocket_ConnectThread -- (instancetype)initWithSourceThread: (OFThread *)sourceThread - socket: (OFTCPSocket *)sock - host: (OFString *)host - port: (uint16_t)port - target: (id)target - selector: (SEL)selector - context: (id)context +@implementation OFTCPSocket_ConnectContext +- (instancetype)initWithSocket: (OFTCPSocket *)sock + host: (OFString *)host + port: (uint16_t)port + target: (id)target + selector: (SEL)selector + context: (id)context { self = [super init]; @try { - _sourceThread = [sourceThread retain]; _socket = [sock retain]; _host = [host copy]; _port = port; _target = [target retain]; _selector = selector; @@ -126,21 +135,19 @@ } return self; } -# ifdef OF_HAVE_BLOCKS -- (instancetype)initWithSourceThread: (OFThread *)sourceThread - socket: (OFTCPSocket *)sock - host: (OFString *)host - port: (uint16_t)port - block: (of_tcp_socket_async_connect_block_t)block +#ifdef OF_HAVE_BLOCKS +- (instancetype)initWithSocket: (OFTCPSocket *)sock + host: (OFString *)host + port: (uint16_t)port + block: (of_tcp_socket_async_connect_block_t)block { self = [super init]; @try { - _sourceThread = [sourceThread retain]; _socket = [sock retain]; _host = [host copy]; _port = port; _block = [block copy]; } @catch (id e) { @@ -148,67 +155,160 @@ @throw e; } return self; } -# endif +#endif - (void)dealloc { - [_sourceThread release]; [_socket release]; [_host release]; [_target release]; [_context release]; -# ifdef OF_HAVE_BLOCKS +#ifdef OF_HAVE_BLOCKS [_block release]; -# endif +#endif [_exception release]; + [_socketAddresses release]; [super dealloc]; } - (void)didConnect { - [self join]; - -# ifdef OF_HAVE_BLOCKS +#ifdef OF_HAVE_BLOCKS if (_block != NULL) _block(_socket, _exception); else { -# endif +#endif void (*func)(id, SEL, OFTCPSocket *, id, id) = (void (*)(id, SEL, OFTCPSocket *, id, id)) [_target methodForSelector: _selector]; func(_target, _selector, _socket, _context, _exception); -# ifdef OF_HAVE_BLOCKS - } -# endif -} - -- (id)main -{ - void *pool = objc_autoreleasePoolPush(); - +#ifdef OF_HAVE_BLOCKS + } +#endif +} + +- (void)socketDidConnect: (OFTCPSocket *)sock + context: (id)context + exception: (id)exception +{ + if (exception != nil) { + if (_socketAddressesIndex >= [_socketAddresses count]) + _exception = [exception retain]; + else { + [self tryNextAddress]; + return; + } + } + + [self didConnect]; +} + +- (void)tryNextAddress +{ + of_socket_address_t address = *(const of_socket_address_t *) + [_socketAddresses itemAtIndex: _socketAddressesIndex++]; + int errNo; + + of_socket_address_set_port(&address, _port); + + if (![_socket of_createSocketForAddress: &address + errNo: &errNo]) { + if (_socketAddressesIndex >= [_socketAddresses count]) { + _exception = [[OFConnectionFailedException alloc] + initWithHost: _host + port: _port + socket: _socket + errNo: errNo]; + [self didConnect]; + return; + } + + [self tryNextAddress]; + return; + } + + [_socket setBlocking: false]; + + if (![_socket of_connectSocketToAddress: &address + errNo: &errNo]) { + if (errNo == EINPROGRESS) { + SEL selector = @selector(socketDidConnect:context: + exception:); + + [OFRunLoop of_addAsyncConnectForTCPSocket: _socket + target: self + selector: selector + context: nil]; + return; + } else { + [_socket of_closeSocket]; + + if (_socketAddressesIndex >= [_socketAddresses count]) { + _exception = [[OFConnectionFailedException + alloc] initWithHost: _host + port: _port + socket: _socket + errNo: errNo]; + [self didConnect]; + return; + } + + [self tryNextAddress]; + return; + } + } + + [self didConnect]; +} + +- (void)resolver: (OFDNSResolver *)resolver + didResolveDomainName: (OFString *)domainName + socketAddresses: (OFData *)socketAddresses + context: (id)context + exception: (id)exception +{ + if (exception != nil) { + _exception = [exception retain]; + [self didConnect]; + return; + } + + _socketAddresses = [socketAddresses copy]; + [self tryNextAddress]; +} + +- (void)start +{ @try { - [_socket connectToHost: _host - port: _port]; - } @catch (id e) { - _exception = [e retain]; - } - - [self performSelector: @selector(didConnect) - onThread: _sourceThread - waitUntilDone: false]; - - objc_autoreleasePoolPop(pool); - - return nil; -} -@end -#endif + of_socket_address_t address = + of_socket_address_parse_ip(_host, _port); + + _socketAddresses = [[OFData alloc] + initWithItems: &address + itemSize: sizeof(address) + count: 1]; + + [self tryNextAddress]; + return; + } @catch (OFInvalidFormatException *e) { + } + + [[OFThread DNSResolver] + asyncResolveSocketAddressesForHost: _host + target: self + selector: @selector(resolver: + didResolveDomainName: + socketAddresses:context: + exception:) + context: nil]; +} +@end @implementation OFTCPSocket @synthesize SOCKS5Host = _SOCKS5Host, SOCKS5Port = _SOCKS5Port; + (void)setSOCKS5Host: (OFString *)host @@ -384,48 +484,46 @@ if (_SOCKS5Host != nil) [self OF_SOCKS5ConnectToHost: destinationHost port: destinationPort]; } -#ifdef OF_HAVE_THREADS - (void)asyncConnectToHost: (OFString *)host port: (uint16_t)port target: (id)target selector: (SEL)selector context: (id)context { + /* TODO: Support SOCKS5 */ void *pool = objc_autoreleasePoolPush(); - [[[[OFTCPSocket_ConnectThread alloc] - initWithSourceThread: [OFThread currentThread] - socket: self - host: host - port: port - target: target - selector: selector - context: context] autorelease] start]; + [[[[OFTCPSocket_ConnectContext alloc] + initWithSocket: self + host: host + port: port + target: target + selector: selector + context: context] autorelease] start]; objc_autoreleasePoolPop(pool); } -# ifdef OF_HAVE_BLOCKS +#ifdef OF_HAVE_BLOCKS - (void)asyncConnectToHost: (OFString *)host port: (uint16_t)port block: (of_tcp_socket_async_connect_block_t)block { + /* TODO: Support SOCKS5 */ void *pool = objc_autoreleasePoolPush(); - [[[[OFTCPSocket_ConnectThread alloc] - initWithSourceThread: [OFThread currentThread] - socket: self - host: host - port: port - block: block] autorelease] start]; + [[[[OFTCPSocket_ConnectContext alloc] + initWithSocket: self + host: host + port: port + block: block] autorelease] start]; objc_autoreleasePoolPop(pool); } -# endif #endif - (uint16_t)bindToHost: (OFString *)host port: (uint16_t)port { Index: tests/Makefile ================================================================== --- tests/Makefile +++ tests/Makefile @@ -51,13 +51,13 @@ OFSHA256HashTests.m \ OFSHA384HashTests.m \ OFSHA512HashTests.m SRCS_PLUGINS = OFPluginTests.m SRCS_SOCKETS = OFDNSResolverTests.m \ + ${OFHTTPCLIENTTESTS_M} \ OFHTTPCookieTests.m \ OFHTTPCookieManagerTests.m \ - ${OFHTTPCLIENTTESTS_M} \ OFKernelEventObserverTests.m \ OFTCPSocketTests.m \ OFUDPSocketTests.m \ SocketTests.m SRCS_THREADS = OFThreadTests.m