[asio] SSL async_shutdown() never finishes if called when async_read_some() is in progress
Hello,
I'm writing a middleware which basically adds an SSL layer to the connection between a client and a server. The connection is bidirectional, so I need to read from each side and write to the other. The client can also close the connection at any time, in which case async_shutdown() must be called on the server's stream.
The problem is, often the shutdown happens when the program is waiting on the server through async_read_some(), and the shutdown will never finish.
The following code illustrates this issue:
#include <iostream>
#include
The only solution I can think of would be to cancel the read operation, but there's no portable way in asio to do this.
sock.next_layer().cancel();
?
Per ASIO Reference: "Calls to cancel() will always fail with boost::asio::error::operation_not_supported when run on Windows XP, Windows Server 2003, and earlier versions of Windows, unless BOOST_ASIO_ENABLE_CANCELIO is defined. However, the CancelIo function has two issues that should be considered before enabling its use: ..." which renders cancel() non-portable.
On Fri, Apr 6, 2018 at 6:11 AM, Alexander D. via Boost-users
The only solution I can think of would be to cancel the read operation, but there's no portable way in asio to do this.
sock.next_layer().cancel();
?
Per ASIO Reference:
"Calls to cancel() will always fail with boost::asio::error::operation_not_supported when run on Windows XP, Windows Server 2003, and earlier versions of Windows, unless BOOST_ASIO_ENABLE_CANCELIO is defined. However, the CancelIo function has two issues that should be considered before enabling its use: ..."
which renders cancel() non-portable.
I consider the issues with cancel present in Windows XP and Windows Server 2003 to be sufficiently problematic that it is not worth taking them into account when designing robust network applications (I do develop mostly on Windows). Unless you have a specific business need to support those defective operating systems, I would just ignore them.
On April 6, 2018 9:55 PM, Vinnie Falco via Boost-users
On Fri, Apr 6, 2018 at 6:11 AM, Alexander D. via Boost-users
wrote: The only solution I can think of would be to cancel the read operation, but there's no portable way in asio to do this.
sock.next_layer().cancel();
?
Per ASIO Reference:
"Calls to cancel() will always fail with boost::asio::error::operation_not_supported when run on Windows XP, Windows Server 2003, and earlier versions of Windows, unless BOOST_ASIO_ENABLE_CANCELIO is defined. However, the CancelIo function has two issues that should be considered before enabling its use: ..."
which renders cancel() non-portable.
I consider the issues with cancel present in Windows XP and Windows Server 2003 to be sufficiently problematic that it is not worth taking them into account when designing robust network applications (I do develop mostly on Windows). Unless you have a specific business need to support those defective operating systems, I would just ignore them.
Yes, I do need the program to work on at least Windows XP. However, even if cancel() is used, it is a workaround, not a solution. If you run the code I supplied, you'll notice that async_read_some() is actually finished with a "stream truncated" error, quite similar to "canceling" the read. The problem is on async_shutdown()'s handler not being called, and it's not mentioned anywhere in the reference. Why can't we shutdown a stream while reading from it (since we can close a socket while reading)?
On 6 April 2018 at 16:29, Alexander D. via Boost-users < boost-users@lists.boost.org> wrote:
On April 6, 2018 9:55 PM, Vinnie Falco via Boost-users < boost-users@lists.boost.org> wrote:
On Fri, Apr 6, 2018 at 6:11 AM, Alexander D. via Boost-users
wrote: The only solution I can think of would be to cancel the read operation, but there's no portable way in asio to do this.
sock.next_layer().cancel();
?
Per ASIO Reference:
"Calls to cancel() will always fail with boost::asio::error::operation_not_supported when run on Windows XP, Windows Server 2003, and earlier versions of Windows, unless BOOST_ASIO_ENABLE_CANCELIO is defined. However, the CancelIo function has two issues that should be considered before enabling its use: ..."
which renders cancel() non-portable.
I consider the issues with cancel present in Windows XP and Windows Server 2003 to be sufficiently problematic that it is not worth taking them into account when designing robust network applications (I do develop mostly on Windows). Unless you have a specific business need to support those defective operating systems, I would just ignore them.
Yes, I do need the program to work on at least Windows XP.
However, even if cancel() is used, it is a workaround, not a solution. If you run the code I supplied, you'll notice that async_read_some() is actually finished with a "stream truncated" error, quite similar to "canceling" the read. The problem is on async_shutdown()'s handler not being called, and it's not mentioned anywhere in the reference. Why can't we shutdown a stream while reading from it (since we can close a socket while reading)?
In the past, when I have had queries about the inner workings and design decisions behind ASIO, I have emailed the author, Chris Karloff. I am sure he is a busy guy but he has taken the trouble to answer helpfully in the past. It may be worth dropping him a line and asking for a suggestion. He didn't charge me but having received help twice now, I think it would be rude of me not to offer him a few hours consultancy if I needed his help again. https://think-async.com/Asio/CommercialSupport
_______________________________________________ Boost-users mailing list Boost-users@lists.boost.org https://lists.boost.org/mailman/listinfo.cgi/boost-users
It's Boris Karloff but Chris(topher) Kohlhoff :-) Am 06.04.2018 um 16:37 schrieb Richard Hodges via Boost-users:
On 6 April 2018 at 16:29, Alexander D. via Boost-users
mailto:boost-users@lists.boost.org> wrote: On April 6, 2018 9:55 PM, Vinnie Falco via Boost-users
mailto:boost-users@lists.boost.org> wrote: > On Fri, Apr 6, 2018 at 6:11 AM, Alexander D. via Boost-users > mailto:boost-users@lists.boost.org> wrote: >>>> The only solution I can think of would be to cancel the read operation, but there's no portable way in asio to do this. >>> >>> sock.next_layer().cancel(); >>> >>> ? >> >> Per ASIO Reference: >> >> "Calls to cancel() will always fail with boost::asio::error::operation_not_supported when run on Windows XP, Windows Server 2003, and earlier versions of Windows, unless BOOST_ASIO_ENABLE_CANCELIO is defined. However, the CancelIo function has two issues that should be considered before enabling its use: >> ..." >> >> which renders cancel() non-portable. > > I consider the issues with cancel present in Windows XP and Windows > Server 2003 to be sufficiently problematic that it is not worth taking > them into account when designing robust network applications (I do > develop mostly on Windows). Unless you have a specific business need > to support those defective operating systems, I would just ignore > them. Yes, I do need the program to work on at least Windows XP.
However, even if cancel() is used, it is a workaround, not a solution. If you run the code I supplied, you'll notice that async_read_some() is actually finished with a "stream truncated" error, quite similar to "canceling" the read. The problem is on async_shutdown()'s handler not being called, and it's not mentioned anywhere in the reference. Why can't we shutdown a stream while reading from it (since we can close a socket while reading)?
In the past, when I have had queries about the inner workings and design decisions behind ASIO, I have emailed the author, Chris Karloff. I am sure he is a busy guy but he has taken the trouble to answer helpfully in the past. It may be worth dropping him a line and asking for a suggestion.
He didn't charge me but having received help twice now, I think it would be rude of me not to offer him a few hours consultancy if I needed his help again.
https://think-async.com/Asio/CommercialSupport
_______________________________________________ Boost-users mailing list Boost-users@lists.boost.org mailto:Boost-users@lists.boost.org https://lists.boost.org/mailman/listinfo.cgi/boost-users https://lists.boost.org/mailman/listinfo.cgi/boost-users
_______________________________________________ Boost-users mailing list Boost-users@lists.boost.org https://lists.boost.org/mailman/listinfo.cgi/boost-users
On Fri, 6 Apr 2018, 19:50 Franz Fehringer via Boost-users, < boost-users@lists.boost.org> wrote:
It's Boris Karloff but Chris(topher) Kohlhoff :-)
Ah yes, important difference. :)
Am 06.04.2018 um 16:37 schrieb Richard Hodges via Boost-users:
On 6 April 2018 at 16:29, Alexander D. via Boost-users < boost-users@lists.boost.org> wrote:
On April 6, 2018 9:55 PM, Vinnie Falco via Boost-users < boost-users@lists.boost.org> wrote:
On Fri, Apr 6, 2018 at 6:11 AM, Alexander D. via Boost-users
wrote: The only solution I can think of would be to cancel the read operation, but there's no portable way in asio to do this.
sock.next_layer().cancel();
?
Per ASIO Reference:
"Calls to cancel() will always fail with boost::asio::error::operation_not_supported when run on Windows XP, Windows Server 2003, and earlier versions of Windows, unless BOOST_ASIO_ENABLE_CANCELIO is defined. However, the CancelIo function has two issues that should be considered before enabling its use: ..."
which renders cancel() non-portable.
I consider the issues with cancel present in Windows XP and Windows Server 2003 to be sufficiently problematic that it is not worth taking them into account when designing robust network applications (I do develop mostly on Windows). Unless you have a specific business need to support those defective operating systems, I would just ignore them.
Yes, I do need the program to work on at least Windows XP.
However, even if cancel() is used, it is a workaround, not a solution. If you run the code I supplied, you'll notice that async_read_some() is actually finished with a "stream truncated" error, quite similar to "canceling" the read. The problem is on async_shutdown()'s handler not being called, and it's not mentioned anywhere in the reference. Why can't we shutdown a stream while reading from it (since we can close a socket while reading)?
In the past, when I have had queries about the inner workings and design decisions behind ASIO, I have emailed the author, Chris Karloff. I am sure he is a busy guy but he has taken the trouble to answer helpfully in the past. It may be worth dropping him a line and asking for a suggestion.
He didn't charge me but having received help twice now, I think it would be rude of me not to offer him a few hours consultancy if I needed his help again.
https://think-async.com/Asio/CommercialSupport
_______________________________________________ Boost-users mailing list Boost-users@lists.boost.org https://lists.boost.org/mailman/listinfo.cgi/boost-users
_______________________________________________ Boost-users mailing listBoost-users@lists.boost.orghttps://lists.boost.org/mailman/listinfo.cgi/boost-users
_______________________________________________ Boost-users mailing list Boost-users@lists.boost.org https://lists.boost.org/mailman/listinfo.cgi/boost-users
On Fri, Apr 6, 2018 at 7:29 AM, Alexander D. via Boost-users
Yes, I do need the program to work on at least Windows XP.
This restriction described in the Boost.Asio documentation places constrains on how your code may be designed: "[CancelIO] will only cancel asynchronous operations that were initiated in the current thread." This means you must always use an implicit strand (only one thread calling io_service::run), and always call initiating functions from it, especially the call to the first initiating function (which is often made from a foreign thread). The call to cancel() must of course be made from the io_service thread (asio sockets are not thread safe), which can be done by using boost::asio::post if you are on a foreign thread. If you need your application service connections using more than one thread you will need to use one io_service per thread.
Why can't we shutdown a stream while reading from it (since we can close a socket while reading)?
If you insist on supporting a defective operating system, then do not be surprised if things don't work the way you want. From the Boost.Asio documentation (RE: CancelIO on Windows XP): "[CancelIO] can appear to complete without error, but the request to cancel the unfinished operations may be silently ignored by the operating system. Whether it works or not seems to depend on the drivers that are installed." So, IF you make sure only one thread is used on each instance of io_service , AND your user has cooperative drivers, then MAYBE your program will function correctly. Regards
OK, after delving into the asio::ssl code, I finally managed to do this by closing the underlying socket when async_read_some() finishes with an error. The crux of this problem is that, when the client sends a "close alert" to the TLS server, the server subsequently closes the connection without sending back a "close alert" (as permitted by TLS standard), causing async_read_some() to catch the error and fail. Meanwhile, async_shutdown() is still blindly waiting for the server to respond, which never happens (ASIO always assumes that the server will respond with a "close alert": https://github.com/boostorg/asio/blob/886839c/include/boost/asio/ssl/detail/...), unless we propagate the error by closing the socket. IMO, asio should probably "remember" the error state of the stream and make all the pending operations fail. At least this avoids some memory leak.
OK, after delving into the asio::ssl code, I finally managed to do
On 7/04/2018 19:02, Alexander D. wrote: this by closing the underlying socket when async_read_some() finishes with an error. Closing the socket on read error is fairly standard practice. Otherwise you have a socket that you're not reading from (which is generally undesirable) or will be endlessly spinning with read errors (if you try to restart the read, since most errors are non-recoverable).
On Fri, 2018-04-06 at 09:11 -0400, Alexander D. via Boost-users wrote:
The only solution I can think of would be to cancel the read operation, but there's no portable way in asio to do this.
sock.next_layer().cancel();
?
Per ASIO Reference:
"Calls to cancel() will always fail with boost::asio::error::operation_not_supported when run on Windows XP, Windows Server 2003, and earlier versions of Windows, unless BOOST_ASIO_ENABLE_CANCELIO is defined. However, the CancelIo function has two issues that should be considered before enabling its use: ..."
which renders cancel() non-portable.
Is anyone still developing for windows xp? It's portable enough in reality, surely?
_______________________________________________ Boost-users mailing list Boost-users@lists.boost.org https://lists.boost.org/mailman/listinfo.cgi/boost-users
participants (5)
-
Alexander D.
-
Franz Fehringer
-
Gavin Lambert
-
Richard Hodges
-
Vinnie Falco