Index: src/OFHTTPClient.m ================================================================== --- src/OFHTTPClient.m +++ src/OFHTTPClient.m @@ -49,15 +49,16 @@ #import "OFUnsupportedVersionException.h" #import "OFWriteFailedException.h" #define REDIRECTS_DEFAULT 10 -@interface OFHTTPClientRequestHandler: OFObject +@interface OFHTTPClientRequestHandler: OFObject { @public OFHTTPClient *_client; OFHTTPRequest *_request; + OFString *_requestString; unsigned int _redirects; id _context; bool _firstLine; OFString *_version; int _status; @@ -276,10 +277,11 @@ - (void)dealloc { [_client release]; [_request release]; + [_requestString release]; [_context release]; [_version release]; [_serverHeaders release]; [super dealloc]; @@ -476,10 +478,12 @@ [_client->_delegate client: _client didReceiveHeaders: _serverHeaders statusCode: _status request: _request context: _context]; + + [sock setDelegate: nil]; [self performSelector: @selector(createResponseWithSocket:) withObject: sock afterDelay: 0]; @@ -521,26 +525,15 @@ forKey: key]; return true; } -- (bool)socket: (OFTCPSocket *)sock +- (bool)stream: (OF_KINDOF(OFStream *))sock didReadLine: (OFString *)line - context: (id)context - exception: (id)exception { bool ret; - if (exception != nil) { - if ([exception isKindOfClass: - [OFInvalidEncodingException class]]) - exception = [OFInvalidServerReplyException exception]; - - [self raiseException: exception]; - return false; - } - @try { if (_firstLine) { _firstLine = false; ret = [self handleFirstLine: line]; } else @@ -552,32 +545,38 @@ } return ret; } -- (size_t)socket: (OFTCPSocket *)sock - didWriteRequest: (const void **)request - length: (size_t)length - context: (id)context - exception: (id)exception -{ - if (exception != nil) { - if ([exception isKindOfClass: [OFWriteFailedException class]] && - ([exception errNo] == ECONNRESET || - [exception errNo] == EPIPE)) { - /* In case a keep-alive connection timed out */ - [self closeAndReconnect]; - return 0; - } - - [self raiseException: exception]; - return 0; - } - +- (void)stream: (OF_KINDOF(OFStream *))sock + didFailWithException: (id)exception +{ + if ([exception isKindOfClass: [OFWriteFailedException class]] && + ([exception errNo] == ECONNRESET || [exception errNo] == EPIPE)) { + /* In case a keep-alive connection timed out */ + [self closeAndReconnect]; + return; + } + + if ([exception isKindOfClass: [OFInvalidEncodingException class]]) + exception = [OFInvalidServerReplyException exception]; + + [self raiseException: exception]; +} + +- (size_t)stream: (OF_KINDOF(OFStream *))sock + didWriteBuffer: (const void **)request + length: (size_t)length +{ _firstLine = true; + + [_requestString release]; + _requestString = nil; if ([[_request headers] objectForKey: @"Content-Length"] != nil) { + [sock setDelegate: nil]; + OFStream *requestBody = [[[OFHTTPClientRequestBodyStream alloc] initWithHandler: self socket: sock] autorelease]; if ([_client->_delegate respondsToSelector: @@ -586,14 +585,11 @@ wantsRequestBody: requestBody request: _request context: _context]; } else - [sock asyncReadLineWithTarget: self - selector: @selector(socket:didReadLine: - context:exception:) - context: nil]; + [sock asyncReadLine]; return 0; } - (void)handleSocket: (OFTCPSocket *)sock @@ -606,22 +602,15 @@ * We do not use the socket's write buffer in case we need to resend * the entire request (e.g. in case a keep-alive connection timed out). */ @try { - OFString *requestString = constructRequestString(_request); - - /* - * Pass requestString as context to retain it so that the - * underlying buffer lives long enough. - */ - [sock asyncWriteBuffer: [requestString UTF8String] - length: [requestString UTF8StringLength] - target: self - selector: @selector(socket:didWriteRequest: - length:context:exception:) - context: requestString]; + [_requestString release]; + _requestString = [constructRequestString(_request) retain]; + + [sock asyncWriteBuffer: [_requestString UTF8String] + length: [_requestString UTF8StringLength]]; } @catch (id e) { [self raiseException: e]; return; } } @@ -632,10 +621,12 @@ { if (exception != nil) { [self raiseException: exception]; return; } + + [sock setDelegate: self]; if ([_client->_delegate respondsToSelector: @selector(client:didCreateSocket:request:context:)]) [_client->_delegate client: _client didCreateSocket: sock @@ -645,46 +636,22 @@ [self performSelector: @selector(handleSocket:) withObject: sock afterDelay: 0]; } -- (bool)throwAwayContent: (OFHTTPClientResponse *)response - buffer: (char *)buffer - length: (size_t)length - context: (OFTCPSocket *)sock - exception: (id)exception -{ - if (exception != nil) { - [self raiseException: exception]; - return false; - } - - if ([response isAtEndOfStream]) { - [self freeMemory: buffer]; - - [_client->_lastResponse release]; - _client->_lastResponse = nil; - - [self performSelector: @selector(handleSocket:) - withObject: sock - afterDelay: 0]; - return false; - } - - return true; -} - - (void)start { OFURL *URL = [_request URL]; OFTCPSocket *sock; /* Can we reuse the last socket? */ if (_client->_socket != nil && ![_client->_socket isAtEndOfStream] && [[_client->_lastURL scheme] isEqual: [URL scheme]] && [[_client->_lastURL host] isEqual: [URL host]] && - [_client->_lastURL port] == [URL port]) { + [_client->_lastURL port] == [URL port] && + (_client->_lastWasHEAD || + [_client->_lastResponse isAtEndOfStream])) { /* * Set _socket to nil, so that in case of an error it won't be * reused. If everything is successful, we set _socket again * at the end. */ @@ -692,31 +659,16 @@ _client->_socket = nil; [_client->_lastURL release]; _client->_lastURL = nil; - if (!_client->_lastWasHEAD && - ![_client->_lastResponse isAtEndOfStream]) { - /* Throw away content that has not been read yet */ - char *buffer = [self allocMemoryWithSize: 512]; - - [_client->_lastResponse - asyncReadIntoBuffer: buffer - length: 512 - target: self - selector: @selector(throwAwayContent: - buffer:length:context: - exception:) - context: sock]; - } else { - [_client->_lastResponse release]; - _client->_lastResponse = nil; - - [self performSelector: @selector(handleSocket:) - withObject: sock - afterDelay: 0]; - } + [_client->_lastResponse release]; + _client->_lastResponse = nil; + + [self performSelector: @selector(handleSocket:) + withObject: sock + afterDelay: 0]; } else [self closeAndReconnect]; } - (void)closeAndReconnect @@ -854,14 +806,12 @@ return; if (_toWrite > 0) @throw [OFTruncatedDataException exception]; - [_socket asyncReadLineWithTarget: _handler - selector: @selector(socket:didReadLine:context: - exception:) - context: nil]; + [_socket setDelegate: _handler]; + [_socket asyncReadLine]; [_socket release]; _socket = nil; } Index: src/OFHTTPServer.m ================================================================== --- src/OFHTTPServer.m +++ src/OFHTTPServer.m @@ -49,12 +49,12 @@ * FIXME: Key normalization replaces headers like "DNT" with "Dnt". * FIXME: Errors are not reported to the user. */ @interface OFHTTPServer () -- (bool)of_socket: (OF_KINDOF(OFTCPSocket *))sock - didAcceptSocket: (OF_KINDOF(OFTCPSocket *))clientSocket +- (bool)of_socket: (OFTCPSocket *)sock + didAcceptSocket: (OFTCPSocket *)clientSocket context: (id)context exception: (id)exception; @end @interface OFHTTPServerResponse: OFHTTPResponse @@ -68,11 +68,11 @@ - (instancetype)initWithSocket: (OF_KINDOF(OFTCPSocket *))sock server: (OFHTTPServer *)server request: (OFHTTPRequest *)request; @end -@interface OFHTTPServer_Connection: OFObject +@interface OFHTTPServer_Connection: OFObject { @public OF_KINDOF(OFTCPSocket *) _socket; OFHTTPServer *_server; OFTimer *_timer; @@ -90,14 +90,10 @@ OFStream *_requestBody; } - (instancetype)initWithSocket: (OF_KINDOF(OFTCPSocket *))sock server: (OFHTTPServer *)server; -- (bool)socket: (OF_KINDOF(OFTCPSocket *))sock - didReadLine: (OFString *)line - context: (id)context - exception: (id)exception; - (bool)parseProlog: (OFString *)line; - (bool)parseHeaders: (OFString *)line; - (bool)sendErrorAndClose: (short)statusCode; - (void)createResponse; @end @@ -407,16 +403,14 @@ [_requestBody release]; [super dealloc]; } -- (bool)socket: (OF_KINDOF(OFTCPSocket *))sock +- (bool)stream: (OF_KINDOF(OFStream *))sock didReadLine: (OFString *)line - context: (id)context - exception: (id)exception { - if (line == nil || exception != nil) + if (line == nil) return false; @try { switch (_state) { case AWAITING_PROLOG: @@ -798,12 +792,12 @@ [_listeningSocket cancelAsyncRequests]; [_listeningSocket release]; _listeningSocket = nil; } -- (bool)of_socket: (OF_KINDOF(OFTCPSocket *))sock - didAcceptSocket: (OF_KINDOF(OFTCPSocket *))clientSocket +- (bool)of_socket: (OFTCPSocket *)sock + didAcceptSocket: (OFTCPSocket *)clientSocket context: (id)context exception: (id)exception { OFHTTPServer_Connection *connection; @@ -818,13 +812,11 @@ connection = [[[OFHTTPServer_Connection alloc] initWithSocket: clientSocket server: self] autorelease]; - [clientSocket asyncReadLineWithTarget: connection - selector: @selector(socket:didReadLine: - context:exception:) - context: nil]; + [clientSocket setDelegate: connection]; + [clientSocket asyncReadLine]; return true; } @end Index: src/OFRunLoop+Private.h ================================================================== --- src/OFRunLoop+Private.h +++ src/OFRunLoop+Private.h @@ -32,36 +32,28 @@ + (void)of_addAsyncReadForStream: (OFStream *) stream buffer: (void *)buffer length: (size_t)length mode: (of_run_loop_mode_t)mode - target: (id)target - selector: (SEL)selector - context: (nullable id)context; + delegate: (id )delegate; + (void)of_addAsyncReadForStream: (OFStream *) stream buffer: (void *)buffer exactLength: (size_t)length mode: (of_run_loop_mode_t)mode - target: (id)target - selector: (SEL)selector - context: (nullable id)context; + delegate: (id )delegate; + (void)of_addAsyncReadLineForStream: (OFStream *) stream encoding: (of_string_encoding_t)encoding mode: (of_run_loop_mode_t)mode - target: (id)target - selector: (SEL)selector - context: (nullable id)context; + delegate: (id )delegate; + (void)of_addAsyncWriteForStream: (OFStream *) stream buffer: (const void *)buffer length: (size_t)length mode: (of_run_loop_mode_t)mode - target: (id)target - selector: (SEL)selector - context: (nullable id)context; + delegate: (id )delegate; + (void)of_addAsyncConnectForTCPSocket: (OFTCPSocket *)socket mode: (of_run_loop_mode_t)mode target: (id)target selector: (SEL)selector context: (nullable id)context; Index: src/OFRunLoop.m ================================================================== --- src/OFRunLoop.m +++ src/OFRunLoop.m @@ -72,10 +72,12 @@ #ifdef OF_HAVE_SOCKETS @interface OFRunLoop_QueueItem: OFObject { @public + id _delegate; + /* TODO: Remove once everything is moved to using delegates */ id _target; SEL _selector; id _context; } @@ -298,10 +300,11 @@ OF_UNRECOGNIZED_SELECTOR } - (void)dealloc { + [_delegate release]; [_target release]; [_context release]; [super dealloc]; } @@ -324,16 +327,26 @@ # ifdef OF_HAVE_BLOCKS if (_block != NULL) return _block(object, _buffer, length, exception); else { # endif - bool (*func)(id, SEL, OFStream *, void *, size_t, id, id) = - (bool (*)(id, SEL, OFStream *, void *, size_t, id, id)) - [_target methodForSelector: _selector]; + if (exception == nil) { + if (![_delegate respondsToSelector: + @selector(stream:didReadIntoBuffer:length:)]) + return false; - return func(_target, _selector, object, _buffer, length, - _context, exception); + return [_delegate stream: object + didReadIntoBuffer: _buffer + length: length]; + } else { + if ([_delegate respondsToSelector: + @selector(stream:didFailWithException:)]) + [_delegate stream: object + didFailWithException: exception]; + + return false; + } # ifdef OF_HAVE_BLOCKS } # endif } @@ -374,20 +387,30 @@ _readLength = 0; return true; } else { # endif - bool (*func)(id, SEL, OFStream *, void *, size_t, id, id) = - (bool (*)(id, SEL, OFStream *, void *, size_t, id, id)) - [_target methodForSelector: _selector]; + if (exception == nil) { + if (![_delegate respondsToSelector: + @selector(stream:didReadIntoBuffer:length:)]) + return false; - if (!func(_target, _selector, object, _buffer, _readLength, - _context, exception)) + if (![_delegate stream: object + didReadIntoBuffer: _buffer + length: _readLength]) + return false; + + _readLength = 0; + return true; + } else { + if ([_delegate respondsToSelector: + @selector(stream:didFailWithException:)]) + [_delegate stream: object + didFailWithException: exception]; + return false; - - _readLength = 0; - return true; + } # ifdef OF_HAVE_BLOCKS } # endif } @@ -420,16 +443,25 @@ # ifdef OF_HAVE_BLOCKS if (_block != NULL) return _block(object, line, exception); else { # endif - bool (*func)(id, SEL, OFStream *, OFString *, id, id) = - (bool (*)(id, SEL, OFStream *, OFString *, id, id)) - [_target methodForSelector: _selector]; + if (exception == nil) { + if (![_delegate respondsToSelector: + @selector(stream:didReadLine:)]) + return false; - return func(_target, _selector, object, line, _context, - exception); + return [_delegate stream: object + didReadLine: line]; + } else { + if ([_delegate respondsToSelector: + @selector(stream:didFailWithException:)]) + [_delegate stream: object + didFailWithException: exception]; + + return false; + } # ifdef OF_HAVE_BLOCKS } # endif } @@ -471,22 +503,32 @@ _writtenLength = 0; return true; } else { # endif - bool (*func)(id, SEL, OFStream *, const void *, size_t, id, - id) = (bool (*)(id, SEL, OFStream *, const void *, size_t, - id, id))[_target methodForSelector: _selector]; - - _length = func(_target, _selector, object, &_buffer, - _writtenLength, _context, exception); - - if (_length == 0) + if (exception == nil) { + if (![_delegate respondsToSelector: + @selector(stream:didWriteBuffer:length:)]) + return false; + + _length = [_delegate stream: object + didWriteBuffer: &_buffer + length: _length]; + + if (_length == 0) + return false; + + _writtenLength = 0; + return true; + } else { + if ([_delegate respondsToSelector: + @selector(stream:didFailWithException:)]) + [_delegate stream: object + didFailWithException: exception]; + return false; - - _writtenLength = 0; - return true; + } # ifdef OF_HAVE_BLOCKS } # endif } @@ -726,18 +768,14 @@ + (void)of_addAsyncReadForStream: (OFStream *) stream buffer: (void *)buffer length: (size_t)length mode: (of_run_loop_mode_t)mode - target: (id)target - selector: (SEL)selector - context: (id)context + delegate: (id )delegate { ADD_READ(OFRunLoop_ReadQueueItem, stream, mode, { - queueItem->_target = [target retain]; - queueItem->_selector = selector; - queueItem->_context = [context retain]; + queueItem->_delegate = [delegate retain]; queueItem->_buffer = buffer; queueItem->_length = length; }) } @@ -744,52 +782,40 @@ + (void)of_addAsyncReadForStream: (OFStream *) stream buffer: (void *)buffer exactLength: (size_t)exactLength mode: (of_run_loop_mode_t)mode - target: (id)target - selector: (SEL)selector - context: (id)context + delegate: (id )delegate { ADD_READ(OFRunLoop_ExactReadQueueItem, stream, mode, { - queueItem->_target = [target retain]; - queueItem->_selector = selector; - queueItem->_context = [context retain]; + queueItem->_delegate = [delegate retain]; queueItem->_buffer = buffer; queueItem->_exactLength = exactLength; }) } + (void)of_addAsyncReadLineForStream: (OFStream *) stream encoding: (of_string_encoding_t)encoding mode: (of_run_loop_mode_t)mode - target: (id)target - selector: (SEL)selector - context: (id)context + delegate: (id )delegate { ADD_READ(OFRunLoop_ReadLineQueueItem, stream, mode, { - queueItem->_target = [target retain]; - queueItem->_selector = selector; - queueItem->_context = [context retain]; + queueItem->_delegate = [delegate retain]; queueItem->_encoding = encoding; }) } + (void)of_addAsyncWriteForStream: (OFStream *) stream buffer: (const void *)buffer length: (size_t)length mode: (of_run_loop_mode_t)mode - target: (id)target - selector: (SEL)selector - context: (id)context + delegate: (id )delegate { ADD_WRITE(OFRunLoop_WriteQueueItem, stream, mode, { - queueItem->_target = [target retain]; - queueItem->_selector = selector; - queueItem->_context = [context retain]; + queueItem->_delegate = [delegate retain]; queueItem->_buffer = buffer; queueItem->_length = length; }) } Index: src/OFStream.h ================================================================== --- src/OFStream.h +++ src/OFStream.h @@ -38,11 +38,12 @@ @class OFStream; @class OFData; #if defined(OF_HAVE_SOCKETS) && defined(OF_HAVE_BLOCKS) /*! - * @brief A block which is called when data was read from the stream. + * @brief A block which is called when data was read asynchronously from a + * stream. * * @param stream The stream on which data was read * @param buffer A buffer with the data that has been read * @param length The length of the data that has been read * @param exception An exception which occurred while reading or `nil` on @@ -51,11 +52,12 @@ */ typedef bool (^of_stream_async_read_block_t)(OF_KINDOF(OFStream *) stream, void *buffer, size_t length, id _Nullable exception); /*! - * @brief A block which is called when a line was read from the stream. + * @brief A block which is called when a line was read asynchronously from a + * stream. * * @param stream The stream on which a line was read * @param line The line which has been read or `nil` when the end of stream * occurred * @param exception An exception which occurred while reading or `nil` on @@ -64,11 +66,12 @@ */ typedef bool (^of_stream_async_read_line_block_t)(OF_KINDOF(OFStream *) stream, OFString *_Nullable line, id _Nullable exception); /*! - * @brief A block which is called when data was written to the stream. + * @brief A block which is called when data was written asynchronously to a + * stream. * * @param stream The stream to which data was written * @param buffer A pointer to the buffer which was written to the stream. This * can be changed to point to a different buffer to be used on the * next write. @@ -77,17 +80,77 @@ * if no exception was encountered. * @param exception An exception which occurred while writing or `nil` on * success * @return The length to repeat the write with or 0 if it should not repeat. * The buffer may be changed, so that every time a new buffer and length - * can be specified while the callback stays the same. + * can be specified */ typedef size_t (^of_stream_async_write_block_t)(OF_KINDOF(OFStream *) stream, const void *_Nonnull *_Nonnull buffer, size_t bytesWritten, id _Nullable exception); #endif +/*! + * @protocol OFStreamDelegate OFStream.h ObjFW/OFStream.h + * + * A delegate for OFStream. + */ +@protocol OFStreamDelegate +@optional +/*! + * @brief This method is called when data was read asynchronously from the + * stream. + * + * @param stream The stream on which data was read + * @param buffer A buffer with the data that has been read + * @param length The length of the data that has been read + * @return A bool whether the read should be repeated + */ +- (bool)stream: (OF_KINDOF(OFStream *))stream + didReadIntoBuffer: (void *)buffer + length: (size_t)length; + +/*! + * @brief This method is called when a line was read asynchronously from the + * stream. + * + * @param stream The stream on which a line was read + * @param line The line which has been read or `nil` when the end of stream + * occurred + * @return A bool whether the read should be repeated + */ +- (bool)stream: (OF_KINDOF(OFStream *))stream + didReadLine: (nullable OFString *)line; + +/*! + * @brief This method is called when data was written asynchronously to the + * stream. + * + * @param stream The stream to which data was written + * @param buffer A pointer to the buffer which was written to the stream. This + * can be changed to point to a different buffer to be used on the + * next write. + * @param length The length of the buffer that has been written + * @return The length to repeat the write with or 0 if it should not repeat. + * The buffer may be changed, so that every time a new buffer and + * length can be specified + */ +- (size_t)stream: (OF_KINDOF(OFStream *))stream + didWriteBuffer: (const void *_Nonnull *_Nonnull)buffer + length: (size_t)length; + +/*! + * @brief This method is called when an exception occurred during an + * asynchronous operation on the stream. + * + * @param stream The stream for which an exception occurred + * @param exception The exception which occurred for the stream + */ +- (void)stream: (OF_KINDOF(OFStream *))stream + didFailWithException: (id)exception; +@end + /*! * @class OFStream OFStream.h ObjFW/OFStream.h * * @brief A base class for different types of streams. * @@ -115,10 +178,11 @@ char *_Nullable _writeBuffer; size_t _readBufferLength, _writeBufferLength; bool _writeBuffered, _waitingForDelimiter; @protected bool _blocking; + id _Nullable _delegate; } /*! * @brief Whether the end of the stream has been reached. */ @@ -140,10 +204,19 @@ * By default, a stream is in blocking mode. * On Win32, setting this currently only works for sockets! */ @property (nonatomic, getter=isBlocking) bool blocking; +/*! + * @brief The delegate for asynchronous operations on the stream. + * + * @note The delegate is retained for as long as asynchronous operations are + * still outstanding. + */ +@property OF_NULLABLE_PROPERTY (assign, nonatomic) + id delegate; + /*! * @brief Reads *at most* size bytes from the stream into a buffer. * * On network streams, this might read less than the specified number of bytes. * If you want to read exactly the specified number of bytes, use @@ -193,26 +266,13 @@ * * @param buffer The buffer into which the data is read. * The buffer must not be freed before the async read completed! * @param length The length of the data that should be read at most. * The buffer *must* be *at least* this big! - * @param target The target on which the selector should be called when the - * data has been received. If the method returns true, it will be - * called again with the same buffer and maximum length when more - * data has been received. If you want the next method in the - * queue to handle the data received next, you need to return - * false from the method. - * @param selector The selector to call on the target. The signature must be - * `bool (OFStream *stream, void *buffer, size_t length, - * id context, id exception)`. - * @param context A context object to pass along to the target */ - (void)asyncReadIntoBuffer: (void *)buffer - length: (size_t)length - target: (id)target - selector: (SEL)selector - context: (nullable id)context; + length: (size_t)length; /*! * @brief Asynchronously reads *at most* size bytes from the stream into a * buffer. * @@ -228,27 +288,14 @@ * @param buffer The buffer into which the data is read. * The buffer must not be freed before the async read completed! * @param length The length of the data that should be read at most. * The buffer *must* be *at least* this big! * @param runLoopMode The run loop mode in which to perform the async read - * @param target The target on which the selector should be called when the - * data has been received. If the method returns true, it will be - * called again with the same buffer and maximum length when more - * data has been received. If you want the next method in the - * queue to handle the data received next, you need to return - * false from the method. - * @param selector The selector to call on the target. The signature must be - * `bool (OFStream *stream, void *buffer, size_t length, - * id context, id exception)`. - * @param context A context object to pass along to the target */ - (void)asyncReadIntoBuffer: (void *)buffer length: (size_t)length - runLoopMode: (of_run_loop_mode_t)runLoopMode - target: (id)target - selector: (SEL)selector - context: (nullable id)context; + runLoopMode: (of_run_loop_mode_t)runLoopMode; /*! * @brief Asynchronously reads exactly the specified length bytes from the * stream into a buffer. * @@ -261,26 +308,13 @@ * for this to work! * * @param buffer The buffer into which the data is read * @param length The length of the data that should be read. * The buffer *must* be *at least* this big! - * @param target The target on which the selector should be called when the - * data has been received. If the method returns true, it will be - * called again with the same buffer and exact length when more - * data has been received. If you want the next method in the - * queue to handle the data received next, you need to return - * false from the method. - * @param selector The selector to call on the target. The signature must be - * `bool (OFStream *stream, void *buffer, size_t length, - * id context, id exception)`. - * @param context A context object to pass along to the target */ - (void)asyncReadIntoBuffer: (void *)buffer - exactLength: (size_t)length - target: (id)target - selector: (SEL)selector - context: (nullable id)context; + exactLength: (size_t)length; /*! * @brief Asynchronously reads exactly the specified length bytes from the * stream into a buffer. * @@ -294,27 +328,14 @@ * * @param buffer The buffer into which the data is read * @param length The length of the data that should be read. * The buffer *must* be *at least* this big! * @param runLoopMode The run loop mode in which to perform the async read - * @param target The target on which the selector should be called when the - * data has been received. If the method returns true, it will be - * called again with the same buffer and exact length when more - * data has been received. If you want the next method in the - * queue to handle the data received next, you need to return - * false from the method. - * @param selector The selector to call on the target. The signature must be - * `bool (OFStream *stream, void *buffer, size_t length, - * id context, id exception)`. - * @param context A context object to pass along to the target */ - (void)asyncReadIntoBuffer: (void *)buffer exactLength: (size_t)length - runLoopMode: (of_run_loop_mode_t)runLoopMode - target: (id)target - selector: (SEL)selector - context: (nullable id)context; + runLoopMode: (of_run_loop_mode_t)runLoopMode; # ifdef OF_HAVE_BLOCKS /*! * @brief Asynchronously reads *at most* ref size bytes from the stream into a * buffer. @@ -776,47 +797,23 @@ * @brief Asynchronously reads until a newline, `\0`, end of stream or an * exception occurs. * * @note The stream must conform to @ref OFReadyForReadingObserving in order * for this to work! - * - * @param target The target on which to call the selector when the data has - * been received. If the method returns true, it will be called - * again when the next line has been received. If you want the - * next method in the queue to handle the next line, you need to - * return false from the method - * @param selector The selector to call on the target. The signature must be - * `bool (OFStream *stream, OFString *line, id context, - * id exception)`. - * @param context A context object to pass along to the target */ -- (void)asyncReadLineWithTarget: (id)target - selector: (SEL)selector - context: (nullable id)context; +- (void)asyncReadLine; /*! * @brief Asynchronously reads with the specified encoding until a newline, * `\0`, end of stream or an exception occurs. * * @note The stream must conform to @ref OFReadyForReadingObserving in order * for this to work! * * @param encoding The encoding used by the stream - * @param target The target on which to call the selector when the data has - * been received. If the method returns true, it will be called - * again when the next line has been received. If you want the - * next method in the queue to handle the next line, you need to - * return false from the method - * @param selector The selector to call on the target. The signature must be - * `bool (OFStream *stream, OFString *line, id context, - * id exception)`. - * @param context A context object to pass along to the target */ -- (void)asyncReadLineWithEncoding: (of_string_encoding_t)encoding - target: (id)target - selector: (SEL)selector - context: (nullable id)context; +- (void)asyncReadLineWithEncoding: (of_string_encoding_t)encoding; /*! * @brief Asynchronously reads with the specified encoding until a newline, * `\0`, end of stream or an exception occurs. * @@ -823,25 +820,13 @@ * @note The stream must conform to @ref OFReadyForReadingObserving in order * for this to work! * * @param encoding The encoding used by the stream * @param runLoopMode The run loop mode in which to perform the async read - * @param target The target on which to call the selector when the data has - * been received. If the method returns true, it will be called - * again when the next line has been received. If you want the - * next method in the queue to handle the next line, you need to - * return false from the method - * @param selector The selector to call on the target. The signature must be - * `bool (OFStream *stream, OFString *line, id context, - * id exception)`. - * @param context A context object to pass along to the target */ - (void)asyncReadLineWithEncoding: (of_string_encoding_t)encoding - runLoopMode: (of_run_loop_mode_t)runLoopMode - target: (id)target - selector: (SEL)selector - context: (nullable id)context; + runLoopMode: (of_run_loop_mode_t)runLoopMode; # ifdef OF_HAVE_BLOCKS /*! * @brief Asynchronously reads until a newline, `\0`, end of stream or an * exception occurs. @@ -985,28 +970,13 @@ * for this to work! * * @param buffer The buffer from which the data is written into the stream. The * buffer needs to be valid until the write request is completed! * @param length The length of the data that should be written - * @param target The target on which the selector should be called when the - * data has been written. The method should return the length for - * the next write with the same callback or 0 if it should not - * repeat. The buffer may be changed, so that every time a new - * buffer and length can be specified while the callback stays - * the same. - * @param selector The selector to call on the target. It should return the - * length for the next write with the same callback or 0 if it - * should not repeat. The signature must be `size_t (OFStream - * *stream, const void *buffer, size_t bytesWritten, id - * context, id exception)`. - * @param context A context object to pass along to the target */ - (void)asyncWriteBuffer: (const void *)buffer - length: (size_t)length - target: (id)target - selector: (SEL)selector - context: (nullable id)context; + length: (size_t)length; /*! * @brief Asynchronously writes a buffer into the stream. * * @note The stream must conform to @ref OFReadyForWritingObserving in order @@ -1014,29 +984,14 @@ * * @param buffer The buffer from which the data is written into the stream. The * buffer needs to be valid until the write request is completed! * @param length The length of the data that should be written * @param runLoopMode The run loop mode in which to perform the async write - * @param target The target on which the selector should be called when the - * data has been written. The method should return the length for - * the next write with the same callback or 0 if it should not - * repeat. The buffer may be changed, so that every time a new - * buffer and length can be specified while the callback stays - * the same. - * @param selector The selector to call on the target. It should return the - * length for the next write with the same callback or 0 if it - * should not repeat. The signature must be `size_t (OFStream - * *stream, const void *buffer, size_t bytesWritten, id - * context, id exception)`. - * @param context A context object to pass along to the target */ - (void)asyncWriteBuffer: (const void *)buffer length: (size_t)length - runLoopMode: (of_run_loop_mode_t)runLoopMode - target: (id)target - selector: (SEL)selector - context: (nullable id)context; + runLoopMode: (of_run_loop_mode_t)runLoopMode; # ifdef OF_HAVE_BLOCKS /*! * @brief Asynchronously writes a buffer into the stream. * Index: src/OFStream.m ================================================================== --- src/OFStream.m +++ src/OFStream.m @@ -61,11 +61,11 @@ #import "of_asprintf.h" #define MIN_READ_SIZE 512 @implementation OFStream -@synthesize of_waitingForDelimiter = _waitingForDelimiter; +@synthesize of_waitingForDelimiter = _waitingForDelimiter, delegate = _delegate; #if defined(SIGPIPE) && defined(SIG_IGN) + (void)initialize { if (self == [OFStream class]) @@ -195,72 +195,50 @@ } #ifdef OF_HAVE_SOCKETS - (void)asyncReadIntoBuffer: (void *)buffer length: (size_t)length - target: (id)target - selector: (SEL)selector - context: (id)context { [self asyncReadIntoBuffer: buffer length: length - runLoopMode: of_run_loop_mode_default - target: target - selector: selector - context: context]; + runLoopMode: of_run_loop_mode_default]; } - (void)asyncReadIntoBuffer: (void *)buffer length: (size_t)length runLoopMode: (of_run_loop_mode_t)runLoopMode - target: (id)target - selector: (SEL)selector - context: (id)context { OFStream *stream = (OFStream *)self; [OFRunLoop of_addAsyncReadForStream: stream buffer: buffer length: length mode: runLoopMode - target: target - selector: selector - context: context]; + delegate: _delegate]; } - (void)asyncReadIntoBuffer: (void *)buffer exactLength: (size_t)length - target: (id)target - selector: (SEL)selector - context: (id)context { [self asyncReadIntoBuffer: buffer exactLength: length - runLoopMode: of_run_loop_mode_default - target: target - selector: selector - context: context]; + runLoopMode: of_run_loop_mode_default]; } - (void)asyncReadIntoBuffer: (void *)buffer exactLength: (size_t)length runLoopMode: (of_run_loop_mode_t)runLoopMode - target: (id)target - selector: (SEL)selector - context: (id)context { OFStream *stream = (OFStream *)self; [OFRunLoop of_addAsyncReadForStream: stream buffer: buffer exactLength: length mode: runLoopMode - target: target - selector: selector - context: context]; + delegate: _delegate]; } # ifdef OF_HAVE_BLOCKS - (void)asyncReadIntoBuffer: (void *)buffer length: (size_t)length @@ -886,48 +864,32 @@ return line; } #ifdef OF_HAVE_SOCKETS -- (void)asyncReadLineWithTarget: (id)target - selector: (SEL)selector - context: (id)context +- (void)asyncReadLine { [self asyncReadLineWithEncoding: OF_STRING_ENCODING_UTF_8 - runLoopMode: of_run_loop_mode_default - target: target - selector: selector - context: context]; + runLoopMode: of_run_loop_mode_default]; } - (void)asyncReadLineWithEncoding: (of_string_encoding_t)encoding - target: (id)target - selector: (SEL)selector - context: (id)context { [self asyncReadLineWithEncoding: encoding - runLoopMode: of_run_loop_mode_default - target: target - selector: selector - context: context]; + runLoopMode: of_run_loop_mode_default]; } - (void)asyncReadLineWithEncoding: (of_string_encoding_t)encoding runLoopMode: (of_run_loop_mode_t)runLoopMode - target: (id)target - selector: (SEL)selector - context: (id)context { OFStream *stream = (OFStream *)self; [OFRunLoop of_addAsyncReadLineForStream: stream encoding: encoding mode: runLoopMode - target: target - selector: selector - context: context]; + delegate: _delegate]; } # ifdef OF_HAVE_BLOCKS - (void)asyncReadLineWithBlock: (of_stream_async_read_line_block_t)block { @@ -1204,39 +1166,28 @@ } #ifdef OF_HAVE_SOCKETS - (void)asyncWriteBuffer: (const void *)buffer length: (size_t)length - target: (id)target - selector: (SEL)selector - context: (id)context { [self asyncWriteBuffer: buffer length: length - runLoopMode: of_run_loop_mode_default - target: target - selector: selector - context: context]; + runLoopMode: of_run_loop_mode_default]; } - (void)asyncWriteBuffer: (const void *)buffer length: (size_t)length runLoopMode: (of_run_loop_mode_t)runLoopMode - target: (id)target - selector: (SEL)selector - context: (id)context { OFStream *stream = (OFStream *)self; [OFRunLoop of_addAsyncWriteForStream: stream buffer: buffer length: length mode: runLoopMode - target: target - selector: selector - context: context]; + delegate: _delegate]; } # ifdef OF_HAVE_BLOCKS - (void)asyncWriteBuffer: (const void *)buffer length: (size_t)length Index: src/OFTCPSocket.m ================================================================== --- src/OFTCPSocket.m +++ src/OFTCPSocket.m @@ -62,11 +62,11 @@ static of_run_loop_mode_t connectRunLoopMode = @"of_tcp_socket_connect_mode"; static OFString *defaultSOCKS5Host = nil; static uint16_t defaultSOCKS5Port = 1080; -@interface OFTCPSocket_AsyncConnectContext: OFObject +@interface OFTCPSocket_AsyncConnectContext: OFObject { OFTCPSocket *_socket; OFString *_host; uint16_t _port; OFString *_SOCKS5Host; @@ -78,12 +78,21 @@ of_tcp_socket_async_connect_block_t _block; #endif id _exception; OFData *_socketAddresses; size_t _socketAddressesIndex; + enum { + SOCKS5_STATE_SEND_AUTHENTICATION = 1, + SOCKS5_STATE_READ_VERSION, + SOCKS5_STATE_SEND_REQUEST, + SOCKS5_STATE_READ_RESPONSE, + SOCKS5_STATE_READ_ADDRESS, + SOCKS5_STATE_READ_ADDRESS_LENGTH, + } _SOCKS5State; /* Longest read is domain name (max 255 bytes) + port */ unsigned char _buffer[257]; + OFMutableData *_request; } - (instancetype)initWithSocket: (OFTCPSocket *)sock host: (OFString *)host port: (uint16_t)port @@ -110,40 +119,10 @@ socketAddresses: (OFData *)socketAddresses context: (id)context exception: (id)exception; - (void)startWithRunLoopMode: (of_run_loop_mode_t)runLoopMode; - (void)sendSOCKS5Request; -- (size_t)socket: (OFTCPSocket *)sock - didSendSOCKS5Authentication: (const void *)request - bytesWritten: (size_t)bytesWritten - context: (id)context - exception: (id)exception; -- (bool)socket: (OFTCPSocket *)sock - didReadSOCKSVersion: (unsigned char *)SOCKSVersion - length: (size_t)length - context: (id)context - exception: (id)exception; -- (size_t)socket: (OFTCPSocket *)sock - didSendSOCKS5Request: (const void *)request - bytesWritten: (size_t)bytesWritten - context: (id)context - exception: (id)exception; -- (bool)socket: (OFTCPSocket *)sock - didReadSOCKS5Response: (unsigned char *)response - length: (size_t)length - context: (id)context - exception: (id)exception; -- (bool)socket: (OFTCPSocket *)sock - didReadSOCKS5Address: (unsigned char *)address - length: (size_t)length - context: (id)context - exception: (id)exception; -- (bool)socket: (OFTCPSocket *)sock - didReadSOCKS5AddressLength: (unsigned char *)addressLength - length: (size_t)length - context: (id)context - exception: (id)exception; @end @interface OFTCPSocket_ConnectContext: OFObject { @public @@ -175,10 +154,12 @@ _SOCKS5Host = [SOCKS5Host copy]; _SOCKS5Port = SOCKS5Port; _target = [target retain]; _selector = selector; _context = [context retain]; + + [_socket setDelegate: self]; } @catch (id e) { [self release]; @throw e; } @@ -211,10 +192,13 @@ } #endif - (void)dealloc { + if ([_socket delegate] == self) + [_socket setDelegate: nil]; + [_socket release]; [_host release]; [_SOCKS5Host release]; [_target release]; [_context release]; @@ -221,16 +205,19 @@ #ifdef OF_HAVE_BLOCKS [_block release]; #endif [_exception release]; [_socketAddresses release]; + [_request release]; [super dealloc]; } - (void)didConnect { + [_socket setDelegate: nil]; + if (_exception == nil) [_socket setBlocking: true]; #ifdef OF_HAVE_BLOCKS if (_block != NULL) @@ -391,258 +378,196 @@ context: nil]; } - (void)sendSOCKS5Request { - [_socket asyncWriteBuffer: "\x05\x01\x00" - length: 3 - runLoopMode: [[OFRunLoop currentRunLoop] currentMode] - target: self - selector: @selector(socket:didSendSOCKS5Authentication: - bytesWritten:context:exception:) - context: nil]; -} - -- (size_t)socket: (OFTCPSocket *)sock - didSendSOCKS5Authentication: (const void *)request - bytesWritten: (size_t)bytesWritten - context: (id)context - exception: (id)exception -{ - if (exception != nil) { - _exception = [exception retain]; - [self didConnect]; - return 0; - } - - [_socket asyncReadIntoBuffer: _buffer - exactLength: 2 - runLoopMode: [[OFRunLoop currentRunLoop] currentMode] - target: self - selector: @selector(socket:didReadSOCKSVersion: - length:context:exception:) - context: nil]; - - return 0; -} - -- (bool)socket: (OFTCPSocket *)sock - didReadSOCKSVersion: (unsigned char *)SOCKSVersion - length: (size_t)length - context: (id)context - exception: (id)exception -{ - OFMutableData *request; - uint8_t hostLength; - unsigned char port[2]; - - if (exception != nil) { - _exception = [exception retain]; - [self didConnect]; - return false; - } - - if (SOCKSVersion[0] != 5 || SOCKSVersion[1] != 0) { - _exception = [[OFConnectionFailedException alloc] - initWithHost: _host - port: _port - socket: self - errNo: EPROTONOSUPPORT]; - [self didConnect]; - return false; - } - - request = [OFMutableData data]; - [request addItems: "\x05\x01\x00\x03" - count: 4]; - - hostLength = (uint8_t)[_host UTF8StringLength]; - [request addItem: &hostLength]; - [request addItems: [_host UTF8String] - count: hostLength]; - - port[0] = _port >> 8; - port[1] = _port & 0xFF; - [request addItems: port - count: 2]; - - /* Use request as context to retain it */ - [_socket asyncWriteBuffer: [request items] - length: [request count] - runLoopMode: [[OFRunLoop currentRunLoop] currentMode] - target: self - selector: @selector(socket:didSendSOCKS5Request: - bytesWritten:context:exception:) - context: request]; - - return false; -} - -- (size_t)socket: (OFTCPSocket *)sock - didSendSOCKS5Request: (const void *)request - bytesWritten: (size_t)bytesWritten - context: (id)context - exception: (id)exception -{ - if (exception != nil) { - _exception = [exception retain]; - [self didConnect]; - return 0; - } - - [_socket asyncReadIntoBuffer: _buffer - exactLength: 4 - runLoopMode: [[OFRunLoop currentRunLoop] currentMode] - target: self - selector: @selector(socket:didReadSOCKS5Response: - length:context:exception:) - context: nil]; - - return 0; -} - -- (bool)socket: (OFTCPSocket *)sock - didReadSOCKS5Response: (unsigned char *)response - length: (size_t)length - context: (id)context - exception: (id)exception -{ - of_run_loop_mode_t runLoopMode; - - if (exception != nil) { - _exception = [exception retain]; - [self didConnect]; - return false; - } - - if (response[0] != 5 || response[2] != 0) { - _exception = [[OFConnectionFailedException alloc] - initWithHost: _host - port: _port - socket: self - errNo: EPROTONOSUPPORT]; - [self didConnect]; - return false; - } - - if (response[1] != 0) { - int errNo; - - switch (response[1]) { - case 0x02: - errNo = EACCES; - break; - case 0x03: - errNo = ENETUNREACH; - break; - case 0x04: - errNo = EHOSTUNREACH; - break; - case 0x05: - errNo = ECONNREFUSED; - break; - case 0x06: - errNo = ETIMEDOUT; - break; - case 0x07: - errNo = EPROTONOSUPPORT; - break; - case 0x08: - errNo = EAFNOSUPPORT; - break; - default: - errNo = EPROTO; - break; - } - - _exception = [[OFConnectionFailedException alloc] - initWithHost: _host - port: _port - socket: _socket - errNo: errNo]; - [self didConnect]; - return false; - } - - runLoopMode = [[OFRunLoop currentRunLoop] currentMode]; - - /* Skip the rest of the response */ - switch (response[3]) { - case 1: /* IPv4 */ - [_socket asyncReadIntoBuffer: _buffer - exactLength: 4 + 2 - runLoopMode: runLoopMode - target: self - selector: @selector(socket: - didReadSOCKS5Address:length: - context:exception:) - context: nil]; - return false; - case 3: /* Domain name */ - [_socket asyncReadIntoBuffer: _buffer - exactLength: 1 - runLoopMode: runLoopMode - target: self - selector: @selector(socket: - didReadSOCKS5AddressLength: - length:context:exception:) - context: nil]; - return false; - case 4: /* IPv6 */ - [_socket asyncReadIntoBuffer: _buffer - exactLength: 16 + 2 - runLoopMode: runLoopMode - target: self - selector: @selector(socket: - didReadSOCKS5Address:length: - context:exception:) - context: nil]; - return false; - default: - _exception = [[OFConnectionFailedException alloc] - initWithHost: _host - port: _port - socket: self - errNo: EPROTONOSUPPORT]; - [self didConnect]; - return false; - } - - return false; -} - -- (bool)socket: (OFTCPSocket *)sock - didReadSOCKS5Address: (unsigned char *)address - length: (size_t)length - context: (id)context - exception: (id)exception -{ - _exception = [exception retain]; - [self didConnect]; - return false; -} - -- (bool)socket: (OFTCPSocket *)sock - didReadSOCKS5AddressLength: (unsigned char *)addressLength - length: (size_t)length - context: (id)context - exception: (id)exception -{ - if (exception != nil) { - _exception = [exception retain]; - [self didConnect]; - return false; - } - - [_socket asyncReadIntoBuffer: _buffer - exactLength: addressLength[0] + 2 - runLoopMode: [[OFRunLoop currentRunLoop] currentMode] - target: self - selector: @selector(socket:didReadSOCKS5Address: - length:context:exception:) - context: nil]; - return false; + _SOCKS5State = SOCKS5_STATE_SEND_AUTHENTICATION; + [_socket asyncWriteBuffer: "\x05\x01\x00" + length: 3 + runLoopMode: [[OFRunLoop currentRunLoop] currentMode]]; +} + +- (bool)stream: (OF_KINDOF(OFStream *))sock + didReadIntoBuffer: (void *)buffer + length: (size_t)length +{ + of_run_loop_mode_t runLoopMode = + [[OFRunLoop currentRunLoop] currentMode]; + unsigned char *SOCKSVersion; + uint8_t hostLength; + unsigned char port[2]; + unsigned char *response, *addressLength; + + switch (_SOCKS5State) { + case SOCKS5_STATE_READ_VERSION: + SOCKSVersion = buffer; + + if (SOCKSVersion[0] != 5 || SOCKSVersion[1] != 0) { + _exception = [[OFConnectionFailedException alloc] + initWithHost: _host + port: _port + socket: self + errNo: EPROTONOSUPPORT]; + [self didConnect]; + return false; + } + + [_request release]; + _request = [[OFMutableData alloc] init]; + + [_request addItems: "\x05\x01\x00\x03" + count: 4]; + + hostLength = (uint8_t)[_host UTF8StringLength]; + [_request addItem: &hostLength]; + [_request addItems: [_host UTF8String] + count: hostLength]; + + port[0] = _port >> 8; + port[1] = _port & 0xFF; + [_request addItems: port + count: 2]; + + _SOCKS5State = SOCKS5_STATE_SEND_REQUEST; + [_socket asyncWriteBuffer: [_request items] + length: [_request count] + runLoopMode: runLoopMode]; + return false; + case SOCKS5_STATE_READ_RESPONSE: + response = buffer; + + if (response[0] != 5 || response[2] != 0) { + _exception = [[OFConnectionFailedException alloc] + initWithHost: _host + port: _port + socket: self + errNo: EPROTONOSUPPORT]; + [self didConnect]; + return false; + } + + if (response[1] != 0) { + int errNo; + + switch (response[1]) { + case 0x02: + errNo = EACCES; + break; + case 0x03: + errNo = ENETUNREACH; + break; + case 0x04: + errNo = EHOSTUNREACH; + break; + case 0x05: + errNo = ECONNREFUSED; + break; + case 0x06: + errNo = ETIMEDOUT; + break; + case 0x07: + errNo = EPROTONOSUPPORT; + break; + case 0x08: + errNo = EAFNOSUPPORT; + break; + default: + errNo = EPROTO; + break; + } + + _exception = [[OFConnectionFailedException alloc] + initWithHost: _host + port: _port + socket: _socket + errNo: errNo]; + [self didConnect]; + return false; + } + + /* Skip the rest of the response */ + switch (response[3]) { + case 1: /* IPv4 */ + _SOCKS5State = SOCKS5_STATE_READ_ADDRESS; + [_socket asyncReadIntoBuffer: _buffer + exactLength: 4 + 2 + runLoopMode: runLoopMode]; + return false; + case 3: /* Domain name */ + _SOCKS5State = SOCKS5_STATE_READ_ADDRESS_LENGTH; + [_socket asyncReadIntoBuffer: _buffer + exactLength: 1 + runLoopMode: runLoopMode]; + return false; + case 4: /* IPv6 */ + _SOCKS5State = SOCKS5_STATE_READ_ADDRESS; + [_socket asyncReadIntoBuffer: _buffer + exactLength: 16 + 2 + runLoopMode: runLoopMode]; + return false; + default: + _exception = [[OFConnectionFailedException alloc] + initWithHost: _host + port: _port + socket: self + errNo: EPROTONOSUPPORT]; + [self didConnect]; + return false; + } + + return false; + case SOCKS5_STATE_READ_ADDRESS: + [self didConnect]; + return false; + case SOCKS5_STATE_READ_ADDRESS_LENGTH: + addressLength = buffer; + + _SOCKS5State = SOCKS5_STATE_READ_ADDRESS; + [_socket asyncReadIntoBuffer: _buffer + exactLength: addressLength[0] + 2 + runLoopMode: runLoopMode]; + return false; + default: + assert(0); + return false; + } +} + +- (size_t)stream: (OF_KINDOF(OFStream *))sock + didWriteBuffer: (const void **)buffer + length: (size_t)length +{ + of_run_loop_mode_t runLoopMode = + [[OFRunLoop currentRunLoop] currentMode]; + + switch (_SOCKS5State) { + case SOCKS5_STATE_SEND_AUTHENTICATION: + _SOCKS5State = SOCKS5_STATE_READ_VERSION; + [_socket asyncReadIntoBuffer: _buffer + exactLength: 2 + runLoopMode: runLoopMode]; + return 0; + case SOCKS5_STATE_SEND_REQUEST: + [_request release]; + _request = nil; + + _SOCKS5State = SOCKS5_STATE_READ_RESPONSE; + [_socket asyncReadIntoBuffer: _buffer + exactLength: 4 + runLoopMode: runLoopMode]; + return 0; + default: + assert(0); + return 0; + } +} + +- (void)stream: (OF_KINDOF(OFStream *))sock + didFailWithException: (id)exception +{ + _exception = [exception retain]; + [self didConnect]; } @end @implementation OFTCPSocket_ConnectContext - (void)dealloc Index: utils/ofhttp/OFHTTP.m ================================================================== --- utils/ofhttp/OFHTTP.m +++ utils/ofhttp/OFHTTP.m @@ -51,11 +51,12 @@ #define GIBIBYTE (1024 * 1024 * 1024) #define MEBIBYTE (1024 * 1024) #define KIBIBYTE (1024) -@interface OFHTTP: OFObject +@interface OFHTTP: OFObject { OFArray OF_GENERIC(OFString *) *_URLs; size_t _URLIndex; int _errorCode; OFString *_outputPath, *_currentFileName; @@ -650,39 +651,14 @@ [self performSelector: @selector(downloadNextURL) afterDelay: 0]; } -- (bool)stream: (OFHTTPResponse *)response +- (bool)stream: (OF_KINDOF(OFStream *))response didReadIntoBuffer: (void *)buffer length: (size_t)length - context: (id)context - exception: (OFException *)e -{ - if (e != nil) { - OFString *URL; - - [_progressBar stop]; - [_progressBar draw]; - [_progressBar release]; - _progressBar = nil; - - if (!_quiet) - [of_stdout writeString: @"\n Error!\n"]; - - URL = [_URLs objectAtIndex: _URLIndex - 1]; - [of_stderr writeLine: - OF_LOCALIZED(@"download_failed_exception", - @"%[prog]: Failed to download <%[url]>: %[exception]", - @"prog", [OFApplication programName], - @"url", URL, - @"exception", e)]; - - _errorCode = 1; - goto next; - } - +{ _received += length; [_output writeBuffer: buffer length: length]; @@ -699,19 +675,42 @@ [of_stdout writeString: @"\n "]; [of_stdout writeLine: OF_LOCALIZED(@"download_done", @"Done!")]; } - goto next; + [self performSelector: @selector(downloadNextURL) + afterDelay: 0]; + return false; } return true; +} + +- (void)stream: (OF_KINDOF(OFStream *))response + didFailWithException: (id)exception +{ + OFString *URL; + + [_progressBar stop]; + [_progressBar draw]; + [_progressBar release]; + _progressBar = nil; + + if (!_quiet) + [of_stdout writeString: @"\n Error!\n"]; + + URL = [_URLs objectAtIndex: _URLIndex - 1]; + [of_stderr writeLine: OF_LOCALIZED( + @"download_failed_exception", + @"%[prog]: Failed to download <%[url]>: %[exception]", + @"prog", [OFApplication programName], + @"url", URL, + @"exception", exception)]; -next: + _errorCode = 1; [self performSelector: @selector(downloadNextURL) afterDelay: 0]; - return false; } - (void)client: (OFHTTPClient *)client didReceiveHeaders: (OFDictionary OF_GENERIC(OFString *, OFString *) *)headers statusCode: (int)statusCode @@ -865,17 +864,13 @@ } [_currentFileName release]; _currentFileName = nil; + [response setDelegate: self]; [response asyncReadIntoBuffer: _buffer - length: [OFSystemInfo pageSize] - target: self - selector: @selector(stream:didReadIntoBuffer: - length:context:exception:) - context: nil]; - + length: [OFSystemInfo pageSize]]; return; next: [_currentFileName release]; _currentFileName = nil;