-
Notifications
You must be signed in to change notification settings - Fork 14
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Refactor handshake #77
base: master
Are you sure you want to change the base?
Changes from 1 commit
eab1d14
66a435c
4ba64de
88ce93c
b437f7a
9971739
0d8b104
c6bb0b0
fd3aea5
7243bad
12aec4a
269342b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,23 +12,21 @@ | |
|
||
#include <boost/wintls/detail/sspi_handshake.hpp> | ||
|
||
#include <boost/asio/coroutine.hpp> | ||
|
||
namespace boost { | ||
namespace wintls { | ||
namespace detail { | ||
|
||
template <typename NextLayer> | ||
struct async_handshake : boost::asio::coroutine { | ||
template<typename NextLayer> | ||
struct async_handshake { | ||
async_handshake(NextLayer& next_layer, detail::sspi_handshake& handshake, handshake_type type) | ||
: next_layer_(next_layer) | ||
, handshake_(handshake) | ||
, entry_count_(0) | ||
, state_(state::idle) { | ||
: next_layer_(next_layer) | ||
, handshake_(handshake) | ||
, entry_count_(0) | ||
, state_(state::idle) { | ||
handshake_(type); | ||
} | ||
|
||
template <typename Self> | ||
template<typename Self> | ||
void operator()(Self& self, boost::system::error_code ec = {}, std::size_t length = 0) { | ||
if (ec) { | ||
self.complete(ec); | ||
|
@@ -40,7 +38,7 @@ struct async_handshake : boost::asio::coroutine { | |
return entry_count_ > 1; | ||
}; | ||
|
||
switch(state_) { | ||
switch (state_) { | ||
case state::reading: | ||
handshake_.size_read(length); | ||
state_ = state::idle; | ||
|
@@ -53,45 +51,33 @@ struct async_handshake : boost::asio::coroutine { | |
break; | ||
} | ||
|
||
detail::sspi_handshake::state handshake_state; | ||
BOOST_ASIO_CORO_REENTER(*this) { | ||
while((handshake_state = handshake_()) != detail::sspi_handshake::state::done) { | ||
if (handshake_state == detail::sspi_handshake::state::data_needed) { | ||
BOOST_ASIO_CORO_YIELD { | ||
state_ = state::reading; | ||
next_layer_.async_read_some(handshake_.in_buffer(), std::move(self)); | ||
} | ||
continue; | ||
} | ||
|
||
if (handshake_state == detail::sspi_handshake::state::data_available) { | ||
BOOST_ASIO_CORO_YIELD { | ||
state_ = state::writing; | ||
net::async_write(next_layer_, handshake_.out_buffer(), std::move(self)); | ||
} | ||
continue; | ||
} | ||
|
||
if (handshake_state == detail::sspi_handshake::state::error) { | ||
if (!is_continuation()) { | ||
BOOST_ASIO_CORO_YIELD { | ||
auto e = self.get_executor(); | ||
net::post(e, [self = std::move(self), ec, length]() mutable { self(ec, length); }); | ||
} | ||
} | ||
self.complete(handshake_.last_error()); | ||
return; | ||
} | ||
switch ((handshake_())) { | ||
case detail::sspi_handshake::state::data_needed: { | ||
state_ = state::reading; | ||
next_layer_.async_read_some(handshake_.in_buffer(), std::move(self)); | ||
return; | ||
} | ||
|
||
if (!is_continuation()) { | ||
BOOST_ASIO_CORO_YIELD { | ||
auto e = self.get_executor(); | ||
net::post(e, [self = std::move(self), ec, length]() mutable { self(ec, length); }); | ||
} | ||
case detail::sspi_handshake::state::data_available: { | ||
state_ = state::writing; | ||
net::async_write(next_layer_, handshake_.out_buffer(), std::move(self)); | ||
return; | ||
} | ||
BOOST_ASSERT(!handshake_.last_error()); | ||
handshake_.manual_auth(); | ||
case detail::sspi_handshake::state::error: { | ||
break; | ||
} | ||
case detail::sspi_handshake::state::done: { | ||
BOOST_ASSERT(!handshake_.last_error()); | ||
handshake_.manual_auth(); | ||
break; | ||
} | ||
} | ||
// If this is the first call to this function, it would cause the completion handler | ||
// (invoked by self.complete()) to be executed on the wrong executor. | ||
// Ensure that doesn't happen by posting the completion handler instead of calling it directly. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @laudrup, i have to admit, i do not fully understand this. I wondered why the code was there and found an early commit where you added it. This comment is based on your commit message then. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess the executor we want is the one associated to our stream and it seems, that async_compose does not know about this executor on construction. If we are not in the first call of this function, the exectutor should be correctly selected by the
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Okay, i think:
I will try to come up with a testcase for this and possibly fix it. Either in this PR or in a new one. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There has been a semantic change in boost 1.81 (see here https://cppalliance.org/boost-release/2022/11/16/KlemensBoost181.html) Basically, you got two executors involved in every op: the one of the io_object (which you can get by if you do a composed op, the first step will get invoked from the initiating function, i.e. synchronously, i.e. before the initiating function returns. That is, when you call stream.async_read([]{}sockect.async_read(...);}); This recursion can now potentially be infinite if the So, we are in a situation, where we initiate the composed op, but we know in the initation how to complete, e.g. we have an error. Then we post so avoid the recursion. Before boost 1.81 you'd post to the executor of the completion, BUT that is peculiar. Let's say you have a socket working on executor1, and the completion is on executor2. Normally, any op on the socket would only complete if executor1 is running, except for some error scenarios. This is weird & inconsistent, so we post to executor1 (the io executor) and complete on executor2. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's important to note that this is not wrong template<typename Self>
void operator()(Self& self, boost::system::error_code ec = {}, std::size_t length = 0) {
if (ec) {
self.complete(ec); Because There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @klemens-morgenstern, thanks lot for this excellent explanation of the problem and the hint regarding the change in boost 1.81! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When you call async_compose you pass in a set of objects: async_compose(implementation, token, objs....); The objs at the end will be used to determine the executors; if the token has none it'll be the same as the io-executor. If you don't spec any objecst the system_executor will be used (which you'll never want). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see!
(or equivalently
Also, if i get everything correctly, we could post to |
||
if (!is_continuation()) { | ||
auto e = self.get_executor(); | ||
net::post(e, [self = std::move(self), ec, length]() mutable { self(ec, length); }); | ||
} else { | ||
self.complete(handshake_.last_error()); | ||
} | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't this supposed to return
void
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a small lambda that returns bool.
Although i do not know why this is a lambda and not a simple bool variable. Technically, even the entry_count_ member could be a bool i suppose.
Then again, looking into the source of asio::detail::composed_op, it seems that it keeps track of its invocations itself and that we could access that via
asio::asio_handler_is_continuation(&self)
. Is that how it should be done?