Index: src/OFHTTPClient.m ================================================================== --- src/OFHTTPClient.m +++ src/OFHTTPClient.m @@ -524,10 +524,17 @@ didWriteBody: (const void **)body length: (size_t)length context: (id)context exception: (id)exception { + if (exception != nil) { + [_client->_delegate client: _client + didEncounterException: exception + forRequest: _request]; + return 0; + } + [socket asyncReadLineWithTarget: self selector: @selector(socket:didReadLine:context: exception:) context: nil]; return 0; @@ -551,11 +558,11 @@ } [_client->_delegate client: _client didEncounterException: exception forRequest: _request]; - return false; + return 0; } if ((body = [_request body]) != nil) { [socket asyncWriteBuffer: [body items] length: [body count] * [body itemSize] Index: src/OFRunLoop+Private.h ================================================================== --- src/OFRunLoop+Private.h +++ src/OFRunLoop+Private.h @@ -56,10 +56,17 @@ buffer: (void *)buffer length: (size_t)length target: (id)target selector: (SEL)selector context: (nullable id)context; ++ (void)of_addAsyncSendForUDPSocket: (OFUDPSocket *)socket + buffer: (const void *)buffer + length: (size_t)length + receiver: (of_udp_socket_address_t)receiver + target: (id)target + selector: (SEL)selector + context: (nullable id)context; # ifdef OF_HAVE_BLOCKS + (void)of_addAsyncReadForStream: (OFStream *)stream buffer: (void *)buffer length: (size_t)length block: (of_stream_async_read_block_t)block; @@ -80,12 +87,17 @@ + (void)of_addAsyncReceiveForUDPSocket: (OFUDPSocket *)socket buffer: (void *)buffer length: (size_t)length block: (of_udp_socket_async_receive_block_t) block; ++ (void)of_addAsyncSendForUDPSocket: (OFUDPSocket *)socket + buffer: (const void *)buffer + length: (size_t)length + receiver: (of_udp_socket_address_t)receiver + block: (of_udp_socket_async_send_block_t)block; # endif + (void)of_cancelAsyncRequestsForObject: (id)object; #endif - (void)of_removeTimer: (OFTimer *)timer; @end OF_ASSUME_NONNULL_END Index: src/OFRunLoop.m ================================================================== --- src/OFRunLoop.m +++ src/OFRunLoop.m @@ -111,10 +111,22 @@ # endif void *_buffer; size_t _length; } @end + +@interface OFRunLoop_UDPSendQueueItem: OFRunLoop_QueueItem +{ +@public +# ifdef OF_HAVE_BLOCKS + of_udp_socket_async_send_block_t _block; +# endif + const void *_buffer; + size_t _length; + of_udp_socket_address_t _receiver; +} +@end @implementation OFRunLoop_QueueItem - (bool)handleObject: (id)object { OF_UNRECOGNIZED_SELECTOR @@ -381,11 +393,11 @@ if (_block != NULL) return _block(object, _buffer, length, address, exception); else { # endif bool (*func)(id, SEL, OFUDPSocket *, void *, size_t, - of_udp_socket_address_t address, id, id) = + of_udp_socket_address_t, id, id) = (bool (*)(id, SEL, OFUDPSocket *, void *, size_t, of_udp_socket_address_t, id, id)) [_target methodForSelector: _selector]; return func(_target, _selector, object, _buffer, length, @@ -392,10 +404,57 @@ address, _context, exception); # ifdef OF_HAVE_BLOCKS } # endif } + +# ifdef OF_HAVE_BLOCKS +- (void)dealloc +{ + [_block release]; + + [super dealloc]; +} +# endif +@end + +@implementation OFRunLoop_UDPSendQueueItem +- (bool)handleObject: (id)object +{ + id exception = nil; + + @try { + [object sendBuffer: _buffer + length: _length + receiver: &_receiver]; + } @catch (id e) { + exception = e; + } + +# ifdef OF_HAVE_BLOCKS + if (_block != NULL) { + _length = _block(object, &_buffer, + (exception == nil ? _length : 0), &_receiver, exception); + + return (_length > 0); + } else { +# endif + size_t (*func)(id, SEL, OFUDPSocket *, const void *, size_t, + of_udp_socket_address_t *, id, id) = + (size_t (*)(id, SEL, OFUDPSocket *, const void *, size_t, + of_udp_socket_address_t *, id, id)) + [_target methodForSelector: _selector]; + + _length = func(_target, _selector, object, &_buffer, + (exception == nil ? _length : 0), &_receiver, _context, + exception); + + return (_length > 0); +# ifdef OF_HAVE_BLOCKS + } +# endif +} # ifdef OF_HAVE_BLOCKS - (void)dealloc { [_block release]; @@ -557,10 +616,28 @@ queueItem->_context = [context retain]; queueItem->_buffer = buffer; queueItem->_length = length; }) } + ++ (void)of_addAsyncSendForUDPSocket: (OFUDPSocket *)socket + buffer: (const void *)buffer + length: (size_t)length + receiver: (of_udp_socket_address_t)receiver + target: (id)target + selector: (SEL)selector + context: (id)context +{ + ADD_WRITE(OFRunLoop_UDPSendQueueItem, socket, { + queueItem->_target = [target retain]; + queueItem->_selector = selector; + queueItem->_context = [context retain]; + queueItem->_buffer = buffer; + queueItem->_length = length; + queueItem->_receiver = receiver; + }) +} # ifdef OF_HAVE_BLOCKS + (void)of_addAsyncReadForStream: (OFStream *)stream buffer: (void *)buffer length: (size_t)length @@ -620,13 +697,27 @@ length: (size_t)length block: (of_udp_socket_async_receive_block_t) block { ADD_READ(OFRunLoop_UDPReceiveQueueItem, socket, { + queueItem->_block = [block copy]; + queueItem->_buffer = buffer; + queueItem->_length = length; + }) +} + ++ (void)of_addAsyncSendForUDPSocket: (OFUDPSocket *)socket + buffer: (const void *)buffer + length: (size_t)length + receiver: (of_udp_socket_address_t)receiver + block: (of_udp_socket_async_send_block_t)block +{ + ADD_WRITE(OFRunLoop_UDPSendQueueItem, socket, { + queueItem->_block = [block copy]; queueItem->_buffer = buffer; queueItem->_length = length; - queueItem->_block = [block copy]; + queueItem->_receiver = receiver; }) } # endif # undef ADD_READ # undef ADD_WRITE Index: src/OFStream.h ================================================================== --- src/OFStream.h +++ src/OFStream.h @@ -65,15 +65,17 @@ /*! * @brief A block which is called when data was written to the stream. * * @param stream The stream to which data was written - * @param buffer The buffer which was written to the stream + * @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 bytesWritten The number of bytes which have been written. This * matches the length specified on the asynchronous write * if no exception was encountered. - * @param exception An exception which occurred while reading or `nil` on + * @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. */ @@ -819,11 +821,11 @@ * 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. The signature must be - * `bool (OFStream *stream, const void *buffer, + * `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 Index: src/OFUDPSocket.h ================================================================== --- src/OFUDPSocket.h +++ src/OFUDPSocket.h @@ -50,11 +50,11 @@ uint16_t port, of_udp_socket_address_t address, id _Nullable exception); /*! * @brief A block which is called when a packet has been received. * - * @param socket The UDP which received a packet + * @param socket The UDP socket which received a packet * @param buffer The buffer the packet has been written to * @param length The length of the packet * @param sender The address of the sender of the packet * @param exception An exception which occurred while receiving or `nil` on * success @@ -61,10 +61,32 @@ * @return A bool whether the same block should be used for the next receive */ typedef bool (^of_udp_socket_async_receive_block_t)(OFUDPSocket *socket, void *buffer, size_t length, of_udp_socket_address_t sender, id _Nullable exception); + +/*! + * @brief A block which is called when a packet has been sent. + * + * @param socket The UDP socket which sent a packet + * @param buffer A pointer to the buffer which was sent. This can be changed to + * point to a different buffer to be used on the next send. + * @param bytesSent The number of bytes which have been sent. This matches the + * length specified on the asynchronous send if no exception + * was encountered. + * @param receiver The receiver for the UDP packet. This may be set to a new + * receiver to which the next packet is sent. + * @param exception An exception which occurred while reading or `nil` on + * success + * @return The length to repeat the send with or 0 if it should not repeat. + * The buffer and receiver may be changed, so that every time a new + * buffer, length and receiver can be specified while the callback + * stays the same. + */ +typedef size_t (^of_udp_socket_async_send_block_t)(OFUDPSocket *socket, + const void *_Nonnull *_Nonnull buffer, size_t bytesSent, + of_udp_socket_address_t *_Nonnull receiver, id exception); #endif /*! * @class OFUDPSocket OFUDPSocket.h ObjFW/OFUDPSocket.h * @@ -203,11 +225,11 @@ * when more datagrams have been received. If you want the next * method in the queue to handle the datagram received next, you * need to return false from the method. * @param selector The selector to call on the target. The signature must be * `bool (OFUDPSocket *socket, void *buffer, size_t length, - * of_udp_socket_address_t, id context, id exception)`. + * of_udp_socket_address_t sender, id context, id exception)`. * @param context A context object to pass along to the target */ - (void)asyncReceiveIntoBuffer: (void *)buffer length: (size_t)length target: (id)target @@ -245,10 +267,56 @@ */ - (void)sendBuffer: (const void *)buffer length: (size_t)length receiver: (const of_udp_socket_address_t *)receiver; +/*! + * @brief Asynchronously sends the specified datagram to the specified address. + * + * @param buffer The buffer to send as a datagram + * @param length The length of the buffer + * @param receiver A pointer to an @ref of_udp_socket_address_t to which the + * datagram should be sent + * @param target The target on which the selector should be called when the + * packet has been sent. The method should return the length for + * the next send with the same callback or 0 if it should not + * repeat. The buffer and receiver may be changed, so that every + * time a new buffer, length and receiver can be specified while + * the callback stays the same. + * @param selector The selector to call on the target. The signature must be + * `size_t (OFUDPSocket *socket, const void **buffer, + * size_t bytesSent, of_udp_socket_address_t *receiver, + * id context, id exception)`. + * @param context A context object to pass along to the target + */ +- (void)asyncSendBuffer: (const void *)buffer + length: (size_t)length + receiver: (of_udp_socket_address_t)receiver + target: (id)target + selector: (SEL)selector + context: (nullable id)context; + +#ifdef OF_HAVE_BLOCKS +/*! + * @brief Asynchronously sends the specified datagram to the specified address. + * + * @param buffer The buffer to send as a datagram + * @param length The length of the buffer + * @param receiver A pointer to an @ref of_udp_socket_address_t to which the + * datagram should be sent + * @param block The block to call when the packet has been sent. It should + * return the length for the next send with the same callback or 0 + * if it should not repeat. The buffer and receiver may be + * changed, so that every time a new buffer, length and receiver + * can be specified while the callback stays the same. + */ +- (void)asyncSendBuffer: (const void *)buffer + length: (size_t)length + receiver: (of_udp_socket_address_t)receiver + block: (of_udp_socket_async_send_block_t)block; +#endif + /*! * @brief Cancels all pending asynchronous requests on the socket. */ - (void)cancelAsyncRequests; Index: src/OFUDPSocket.m ================================================================== --- src/OFUDPSocket.m +++ src/OFUDPSocket.m @@ -630,10 +630,40 @@ @throw [OFWriteFailedException exceptionWithObject: self requestedLength: length bytesWritten: bytesWritten errNo: 0]; } + +- (void)asyncSendBuffer: (const void *)buffer + length: (size_t)length + receiver: (of_udp_socket_address_t)receiver + target: (id)target + selector: (SEL)selector + context: (id)context +{ + [OFRunLoop of_addAsyncSendForUDPSocket: self + buffer: buffer + length: length + receiver: receiver + target: target + selector: selector + context: context]; +} + +#ifdef OF_HAVE_BLOCKS +- (void)asyncSendBuffer: (const void *)buffer + length: (size_t)length + receiver: (of_udp_socket_address_t)receiver + block: (of_udp_socket_async_send_block_t)block +{ + [OFRunLoop of_addAsyncSendForUDPSocket: self + buffer: buffer + length: length + receiver: receiver + block: block]; +} +#endif - (void)cancelAsyncRequests { [OFRunLoop of_cancelAsyncRequestsForObject: self]; }