Boost logo

Boost :

From: Mats Nilsson (mats.nilsson_at_[hidden])
Date: 2005-12-23 04:45:58


Hi

I have been enjoying reading reviews of asio, and I have played with it a
little bit for the purpose of possibly using it for implementing various
protocols, many of which are layered in various ways. It would be nice to
find a systematic way to decompose the layers involved.

I've been thinking about how to do this in a way similar to the ACE Streams
framework, without having to resort to runtime polymorphism and type
erasure, but have not found a clean way to do this.

This post made me think about this differently.

Christopher Kohlhoff wrote:
> For example, I could easily imagine a function:
>
> template <typename Handler>
> void async_http_get_content(
> asio::demuxer& demuxer,
> const std::string& url,
> std::vector<char>& content,
> Handler handler);
>
> Internally this might parse the URL, resolve the host name,
> create a socket, connect, transmit request, collect response,
> close socket and finally invoke the application handler, all
> asynchronously.

I think this is very useful, so to be able to assemble this and other higher
abstractions one would likely need to be able to compose in terms of more
fundamental building blocks.

For instance:

// Asynchronously get a line of input from 'stream'
template <typename BUFFER, typename DISPATCHER, typename HANDLER>
void async_get_line(
        BUFFER& buffered_stream,
      DISPATCHER& dispatcher,
        std::vector<char>& line,
        Handler handler);

// Asynchronously get http headers, by repeatedly using async_get_line
template <typename BUFFER, typename DISPATCHER, typename HANDLER>
void asych_get_http_headers(
        BUFFER& buffered_stream,
      DISPATCHER& dispatcher,
        std::map<std::string, std::string> >& headers,
        Handler handler);

// Asynchronously get the http response body
template <typename BUFFER, typename DISPATCHER, typename HANDLER>
void asych_get_http_body(
        BUFFER& buffered_stream,
      DISPATCHER& dispatcher,
        std::vector<char>& body,
        size_t content_length,
        Handler handler);

Now, asynch_get_line would not know how much to consume from the socket, and
getting one character at a time would be inappropriate. Thus, I added
another template parameter BUFFER that is a fictional concept for the
purpose of this discussion that supports pushing back unconsumed data for
later invocations. I see that there is an asio::buffered_stream. Maybe it
can be changed to support pushing back data.

It would be driven like this

asio::socket_stream s;
buffer<asio::socket_stream> buffer(s);
std::vector<char> body;
std::map<std::string, std::string> > headers;

... // Send request, then
void request_sent(...) {
        async_get_http_headers(buffer, demuxer, headers,
handle_read_headers);
}

void handle_read_headers(...) {
        size_t content_length = atoi(headers["content-length"]);
        asynch_get_http_body(buffer, demuxer, body,
                content_length, handle_read_body);
}

Do you think this is a fruitful way to proceed? Will this sort decomposition
be unfavorable with respect to performance?

Also expressing these abstract operations in general enough types allows
driving a protocol stack from a different source, such as a test driver
instead of a specific socket type.

You have already defined concepts for many aspects of asio. Could the
suggested BUFFER concept derive from the Stream concept? What would that
entail for the implementation of e.g asio::buffered_stream? I'm thinking
about STL and the way that iterators concepts are hierarchically derived
from each other and that algorithms are expressed in terms of a certain
iterator concept.

Speaking of concepts. I feel a little bit lost navigating the template type
hierarchies. Consistent reuse of concept names where appropriate might help.
For instance, you define a concept 'Dispatcher', yet you use the name
demuxer_type everywhere.

Finally, I don't see any way to cancel asynchronous operations. While this
may be ok for the read_some functions, things get more serious when
composing larger operations.

What are your thoughts around this? Has cancellation been discussed before?

 
Thanks

Mats


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk