Re: [asio-users] Any solution for polymorphic SSL stream support?
Brought to you by:
chris_kohlhoff
From: Vinnie F. <vin...@gm...> - 2017-08-15 03:24:19
|
On Mon, Aug 14, 2017 at 8:08 PM, Aaron Koolen-Bourke <aar...@gm...> wrote: > If SSL requires that you have to use it in a strand or else it breaks (And > it's running asynchronously), why shouldn't it deal with that itself "Separation of concerns" <https://en.wikipedia.org/wiki/Separation_of_concerns> SSL does not require that you use a strand, it only requires that you use an appropriate technique to prevent the stream object from being accessed in a way that breaks invariants. There are several ways to do this: 1. Only call io_service::run from a single thread 2. Use an explicit strand of type io_service::strand 3. Use a user-defined type and dispatching mechanism For 3 this is accomplished with suitable overloads of asio_handler_invoke for the user defined type. Number 1 is popular, and if you create a separate io_service for each thread (distributing the streams evenly between the set of io_service objects) you can get high performance without the need for explicit strands. My point is that your idea to push the responsibility of safe access on to ssl::stream prevents choices 1 and 3 above. > (or have some mechanism for supporting it within the SSL stream). That mechanism already exists, it is called asio_handler_invoke and it is a "customization point" <http://www.boost.org/doc/libs/1_64_0/doc/html/boost_asio/reference/asio_handler_invoke.html> <http://ericniebler.com/2014/10/21/customization-point-design-in-c11-and-beyond/> > if you have a layer of streams you need to have the strand at the client > call and suffer the performance overhead at every place in the chain just > because one step in the chain requires it. That is incorrect, the overhead is the same no matter the number of streams. The performance characteristic you described, of decreasing performance per wrapper, is more applicable to std::future based completion notification mechanisms. Completion handlers were designed to be composable in precisely the manner I outlined in my 4-wrapper example. A full explanation of performance for completion notification of nested streams is given in "A Universal Model for Asynchronous Operations" written by the Asio author (Christopher Kohlhoff) in N3747 <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3747.pdf> > some thought about chaining and performance would have gone a long way. Have you done any performance measurements? > I'm not entirely sure of what the best solution is (type traited system > to chose wrappers for each party in the chain?) but certainly what it > is now, for the use case I have, it does seem to be troublesome. Why? > Your example of 4 streams is nice but in reality the stream (in my case) is > built up over time. I don't know the full chain right at connection start. > Some layers may be added/inserted and some may be removed. So? Use move-construction to assemble new streams from existing ones. I do this in the examples I linked. Here, an HTTP session is turned into a WebSocket sesison: <https://github.com/boostorg/beast/blob/d337339c028f247a91a9b2e771e7b31a085496aa/example/advanced/server/advanced_server.cpp#L532> SSL-capable version: <https://github.com/boostorg/beast/blob/d337339c028f247a91a9b2e771e7b31a085496aa/example/advanced/server-flex/advanced_server_flex.cpp#L718> To get around boost::asio::ssl::stream limitation on move construction, I have created my own movable ssl::stream wrapper: <https://github.com/boostorg/beast/blob/d337339c028f247a91a9b2e771e7b31a085496aa/example/common/ssl_stream.hpp#L23> The wrapper will not be necessary once Boost.Asio is updated. > Even without that complexity, one would start with a socket, upon SSL > detection create a new connection (more heap allocs and moving of members), > then after CONNECT there might be another SSL for inspection, so another > connection creation with another layered type. So what? Are you complaining about the need for heap allocations for an event that occurs only a few times for the lifetime of a connection? Have you measured the impact on performance? Are you aware that every time you call an asynchronous initiating function, Asio calls asio_handler_allocate to allocate memory to store the handler? How much does calling the allocation function a few more times for the lifetime of a connection impact performance measurably if at all? > I realise there's many ways to solve this problem and a pure data pipeline > (Outside of the actual socket/stream) might be best for some of this, It sounds to me like you 1. already have a design in mind, and 2. have made unfounded performance assumptions about other approaches. I would suggest a more evidence-based approach, and being open-minded to established practice which comes from available, working examples. Thanks |