Index: src/OFGZIPStream.m ================================================================== --- src/OFGZIPStream.m +++ src/OFGZIPStream.m @@ -304,17 +304,16 @@ @throw [OFNotOpenException exceptionWithObject: self]; return _stream.atEndOfStream; } -- (bool)hasDataInReadBuffer +- (bool)lowlevelHasDataInReadBuffer { if (_state == OFGZIPStreamStateData) - return (super.hasDataInReadBuffer || - _inflateStream.hasDataInReadBuffer); - - return (super.hasDataInReadBuffer || _stream.hasDataInReadBuffer); + return _inflateStream.hasDataInReadBuffer; + else + return _stream.hasDataInReadBuffer; } - (void)close { if (_stream == nil) Index: src/OFHTTPClient.m ================================================================== --- src/OFHTTPClient.m +++ src/OFHTTPClient.m @@ -1066,13 +1066,13 @@ return ((OFStream *)_stream) .fileDescriptorForReading; } -- (bool)hasDataInReadBuffer +- (bool)lowlevelHasDataInReadBuffer { - return (super.hasDataInReadBuffer || _stream.hasDataInReadBuffer); + return _stream.hasDataInReadBuffer; } - (void)close { if (_stream == nil) Index: src/OFHTTPServer.m ================================================================== --- src/OFHTTPServer.m +++ src/OFHTTPServer.m @@ -733,13 +733,13 @@ return 0; } } -- (bool)hasDataInReadBuffer +- (bool)lowlevelHasDataInReadBuffer { - return (super.hasDataInReadBuffer || _socket.hasDataInReadBuffer); + return _socket.hasDataInReadBuffer; } - (int)fileDescriptorForReading { return _socket.fileDescriptorForReading; Index: src/OFInflateStream.m ================================================================== --- src/OFInflateStream.m +++ src/OFInflateStream.m @@ -672,13 +672,13 @@ { return ((id )_stream) .fileDescriptorForReading; } -- (bool)hasDataInReadBuffer +- (bool)lowlevelHasDataInReadBuffer { - return (super.hasDataInReadBuffer || _stream.hasDataInReadBuffer || + return (_stream.hasDataInReadBuffer || _bufferLength - _bufferIndex > 0); } - (void)close { Index: src/OFKernelEventObserver.m ================================================================== --- src/OFKernelEventObserver.m +++ src/OFKernelEventObserver.m @@ -206,15 +206,20 @@ { void *pool = objc_autoreleasePoolPush(); bool foundInReadBuffer = false; for (id object in [[_readObjects copy] autorelease]) { - void *pool2 = objc_autoreleasePoolPush(); + void *pool2; + + if (![object isKindOfClass: [OFStream class]]) + continue; + + pool2 = objc_autoreleasePoolPush(); - if ([object isKindOfClass: [OFStream class]] && - [object hasDataInReadBuffer] && - ![(OFStream *)object of_isWaitingForDelimiter]) { + if ([object hasDataInReadBuffer] && + (![object of_isWaitingForDelimiter] || + [object lowlevelHasDataInReadBuffer])) { if ([_delegate respondsToSelector: @selector(objectIsReadyForReading:)]) [_delegate objectIsReadyForReading: object]; foundInReadBuffer = true; Index: src/OFLHADecompressingStream.m ================================================================== --- src/OFLHADecompressingStream.m +++ src/OFLHADecompressingStream.m @@ -505,13 +505,13 @@ { return ((id )_stream) .fileDescriptorForReading; } -- (bool)hasDataInReadBuffer +- (bool)lowlevelHasDataInReadBuffer { - return (super.hasDataInReadBuffer || _stream.hasDataInReadBuffer || + return (_stream.hasDataInReadBuffer || _bufferLength - _bufferIndex > 0); } - (void)close { Index: src/OFStream.h ================================================================== --- src/OFStream.h +++ src/OFStream.h @@ -1389,8 +1389,20 @@ * implementation when subclassing! * * @return Whether the lowlevel is at the end of the stream */ - (bool)lowlevelIsAtEndOfStream; + +/** + * @brief Returns whether the lowlevel has data in the read buffer. + * + * @warning Do not call this directly! + * + * @note Override this method in case your stream can buffer data itself, such + * as when implementing @ref OFTLSStream. If not overridden, it always + * returns false. + * @return Whether the lowlevel has data in the read buffer + */ +- (bool)lowlevelHasDataInReadBuffer; @end OF_ASSUME_NONNULL_END Index: src/OFStream.m ================================================================== --- src/OFStream.m +++ src/OFStream.m @@ -94,24 +94,29 @@ OFFreeMemory(_writeBuffer); [super dealloc]; } -- (bool)lowlevelIsAtEndOfStream -{ - OF_UNRECOGNIZED_SELECTOR -} - - (size_t)lowlevelReadIntoBuffer: (void *)buffer length: (size_t)length { OF_UNRECOGNIZED_SELECTOR } - (size_t)lowlevelWriteBuffer: (const void *)buffer length: (size_t)length { OF_UNRECOGNIZED_SELECTOR } + +- (bool)lowlevelIsAtEndOfStream +{ + OF_UNRECOGNIZED_SELECTOR +} + +- (bool)lowlevelHasDataInReadBuffer +{ + return false; +} - (id)copy { return [self retain]; } @@ -1184,11 +1189,11 @@ } } - (bool)hasDataInReadBuffer { - return (_readBufferLength > 0); + return (_readBufferLength > 0 || [self lowlevelHasDataInReadBuffer]); } - (bool)canBlock { return _canBlock; Index: src/OFTLSStream.h ================================================================== --- src/OFTLSStream.h +++ src/OFTLSStream.h @@ -59,17 +59,16 @@ * * This class is a class cluster and returns a suitable OFTLSStream subclass, * if available. * * Subclasses need to override @ref lowlevelReadIntoBuffer:length:, - * @ref lowlevelWriteBuffer:length: and - * @ref asyncPerformClientHandshakeWithHost:runLoopMode:. The method - * @ref hasDataInReadBuffer should be overridden to return `true` if the TLS - * stream has cached unprocessed data internally, while returning - * `self.underlyingStream.hasDataInReadBuffer` if it does not have any - * unprocessed data. In order to get access to the underlying stream, - * @ref underlyingStream can be used. + * @ref lowlevelWriteBuffer:length:, + * @ref lowlevelHasDataInReadBuffer and + * @ref asyncPerformClientHandshakeWithHost:runLoopMode:. + * + * In order to get access to the underlying stream, @ref underlyingStream can + * be used. */ @interface OFTLSStream: OFStream { OFStream @@ -107,11 +106,11 @@ * @param stream The stream to use as underlying stream. Must not be closed * before the TLS stream is closed. * @return A new, autoreleased TLS stream */ + (instancetype)streamWithStream: (OFStream *)stream; + OFReadyForWritingObserving> *)stream; /** * @brief Initializes the TLS stream with the specified stream as its * underlying stream. * @@ -118,11 +117,11 @@ * @param stream The stream to use as underlying stream. Must not be closed * before the TLS stream is closed. * @return An initialized TLS stream */ - (instancetype)initWithStream: (OFStream *)stream + OFReadyForWritingObserving> *)stream OF_DESIGNATED_INITIALIZER; /** * @brief Asynchronously performs the TLS client handshake for the specified * host and calls the delegate afterwards. Index: src/OFTLSStream.m ================================================================== --- src/OFTLSStream.m +++ src/OFTLSStream.m @@ -139,16 +139,10 @@ - (size_t)lowlevelWriteBuffer: (const void *)buffer length: (size_t)length { OF_UNRECOGNIZED_SELECTOR } -- (bool)hasDataInReadBuffer -{ - return (super.hasDataInReadBuffer || - _underlyingStream.hasDataInReadBuffer); -} - - (bool)lowlevelIsAtEndOfStream { return _underlyingStream.atEndOfStream; } Index: src/OFTarArchive.m ================================================================== --- src/OFTarArchive.m +++ src/OFTarArchive.m @@ -360,13 +360,13 @@ @throw [OFNotOpenException exceptionWithObject: self]; return _atEndOfStream; } -- (bool)hasDataInReadBuffer +- (bool)lowlevelHasDataInReadBuffer { - return (super.hasDataInReadBuffer || _stream.hasDataInReadBuffer); + return _stream.hasDataInReadBuffer; } - (int)fileDescriptorForReading { return ((id )_stream) Index: src/OFZIPArchive.m ================================================================== --- src/OFZIPArchive.m +++ src/OFZIPArchive.m @@ -939,14 +939,13 @@ } return ret; } -- (bool)hasDataInReadBuffer +- (bool)lowlevelHasDataInReadBuffer { - return (super.hasDataInReadBuffer || - [_decompressedStream hasDataInReadBuffer]); + return _decompressedStream.hasDataInReadBuffer; } - (int)fileDescriptorForReading { return ((id )_decompressedStream) Index: src/tls/OFGnuTLSTLSStream.m ================================================================== --- src/tls/OFGnuTLSTLSStream.m +++ src/tls/OFGnuTLSTLSStream.m @@ -17,11 +17,10 @@ #include #import "OFGnuTLSTLSStream.h" #import "OFData.h" -#import "OFStream+Private.h" #import "OFAlreadyOpenException.h" #import "OFInitializationFailedException.h" #import "OFNotOpenException.h" #import "OFReadFailedException.h" @@ -184,32 +183,14 @@ } return ret; } -- (bool)hasDataInReadBuffer -{ - if (gnutls_record_check_pending(_session) > 0) - return true; - - return super.hasDataInReadBuffer; -} - -- (bool)of_isWaitingForDelimiter -{ - /* FIXME: There should be a non-private API for this. */ - - /* - * If we still have pending data in the session, we haven't processed - * it yet to see if our delimiter is in there. So return false here, as - * that will signal the stream as ready for reading, which in turn will - * cause a read and checking for the delimiter. - */ - if (gnutls_record_check_pending(_session) > 0) - return false; - - return super.of_waitingForDelimiter; +- (bool)lowlevelHasDataInReadBuffer +{ + return (_underlyingStream.hasDataInReadBuffer || + gnutls_record_check_pending(_session) > 0); } - (void)asyncPerformClientHandshakeWithHost: (OFString *)host runLoopMode: (OFRunLoopMode)runLoopMode { Index: src/tls/OFOpenSSLTLSStream.m ================================================================== --- src/tls/OFOpenSSLTLSStream.m +++ src/tls/OFOpenSSLTLSStream.m @@ -17,11 +17,10 @@ #include #import "OFOpenSSLTLSStream.h" #import "OFData.h" -#import "OFStream+Private.h" #import "OFAlreadyOpenException.h" #import "OFInitializationFailedException.h" #import "OFNotOpenException.h" #import "OFReadFailedException.h" @@ -195,42 +194,14 @@ } return bytesWritten; } -- (bool)hasDataInReadBuffer -{ - if (SSL_pending(_SSL) > 0 || BIO_ctrl_pending(_readBIO) > 0) - return true; - - return super.hasDataInReadBuffer; -} - -- (bool)of_isWaitingForDelimiter -{ - /* FIXME: There should be a non-private API for this. */ - - /* - * If we still have pending data in the SSL connection, we haven't - * processed it yet to see if our delimiter is in there. So return - * false here, as that will signal the stream as ready for reading, - * which in turn will cause a read and checking for the delimiter. - */ - if (SSL_pending(_SSL)) - return false; - - /* - * If we still have data in our read BIO, it hasn't been processed by - * OpenSSL yet. As we have no idea what's in there, return false to - * signal the stream as ready for reading, which in turn will cause a - * read to check for the delimiter and in turn make OpenSSL process the - * data in the read BIO. - */ - if (BIO_ctrl_pending(_readBIO) > 0) - return false; - - return super.of_waitingForDelimiter; +- (bool)lowlevelHasDataInReadBuffer +{ + return (_underlyingStream.hasDataInReadBuffer || + SSL_has_pending(_SSL) || BIO_ctrl_pending(_readBIO) > 0); } - (void)asyncPerformClientHandshakeWithHost: (OFString *)host runLoopMode: (OFRunLoopMode)runLoopMode { Index: src/tls/OFSecureTransportTLSStream.m ================================================================== --- src/tls/OFSecureTransportTLSStream.m +++ src/tls/OFSecureTransportTLSStream.m @@ -16,11 +16,10 @@ #include "config.h" #include #import "OFSecureTransportTLSStream.h" -#import "OFStream+Private.h" #import "OFAlreadyOpenException.h" #import "OFNotOpenException.h" #import "OFReadFailedException.h" #import "OFTLSHandshakeFailedException.h" @@ -167,38 +166,17 @@ errNo: 0]; return bytesWritten; } -- (bool)hasDataInReadBuffer -{ - size_t bufferSize; - - if (SSLGetBufferedReadSize(_context, &bufferSize) == noErr && - bufferSize > 0) - return true; - - return super.hasDataInReadBuffer; -} - -- (bool)of_isWaitingForDelimiter -{ - size_t bufferSize; - - /* FIXME: There should be a non-private API for this. */ - - /* - * If we still have pending data in the context, we haven't processed - * it yet to see if our delimiter is in there. So return false here, as - * that will signal the stream as ready for reading, which in turn will - * cause a read and checking for the delimiter. - */ - if (SSLGetBufferedReadSize(_context, &bufferSize) == noErr && - bufferSize > 0) - return false; - - return super.of_waitingForDelimiter; +- (bool)lowlevelHasDataInReadBuffer +{ + size_t bufferSize; + + return (_underlyingStream.hasDataInReadBuffer || + (SSLGetBufferedReadSize(_context, &bufferSize) == noErr && + bufferSize > 0)); } - (void)asyncPerformClientHandshakeWithHost: (OFString *)host runLoopMode: (OFRunLoopMode)runLoopMode {