Skip to content

Commit 3bad001

Browse files
committed
Adding Overloads for Options on connection::write
This commit enables the user to call 'write' with a special callback function. Instead of just passing in a range, the user can then provide a callback function once the writes are done. This solution allows for writing handlers that by themselves can chunk up responses to conserve memory on their own. An example streaming server using this approach should be doable. This implementation is largely untested, but builds. Also, setting the headers will by default send out the headers. In case any errors are encountered at the time of the sending out of the headers, that will be saved and then thrown in subsequent calls to 'write'. This commit also contains a change to the macro that determines the buffer size for connections, and for the chunk sizes of the linearized buffers to reflect the correct type.
1 parent a73d403 commit 3bad001

File tree

2 files changed

+138
-9
lines changed

2 files changed

+138
-9
lines changed

boost/network/protocol/http/server/async_connection.hpp

Lines changed: 137 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,16 @@
1010
#include <boost/scope_exit.hpp>
1111
#include <boost/network/protocol/http/algorithms/linearize.hpp>
1212
#include <boost/network/utils/thread_pool.hpp>
13+
#include <boost/range/adaptor/sliced.hpp>
1314
#include <boost/range/algorithm/transform.hpp>
15+
#include <boost/range/algorithm/copy.hpp>
1416
#include <boost/asio/ip/tcp.hpp>
1517
#include <boost/asio/streambuf.hpp>
1618
#include <boost/asio/strand.hpp>
1719
#include <boost/asio/buffer.hpp>
20+
#include <boost/make_shared.hpp>
21+
#include <list>
22+
#include <vector>
1823
#include <iterator>
1924

2025
#ifndef BOOST_NETWORK_HTTP_SERVER_CONNECTION_HEADER_BUFFER_MAX_SIZE
@@ -53,6 +58,8 @@ namespace boost { namespace network { namespace http {
5358
, service_unavailable = 503
5459
};
5560

61+
typedef typename string<Tag>::type string_type;
62+
5663
async_connection(
5764
asio::io_service & io_service
5865
, Handler & handler
@@ -76,7 +83,7 @@ namespace boost { namespace network { namespace http {
7683
* then sent as soon as the first call to `write` or `flush` commences.
7784
*/
7885
template <class Range>
79-
void set_headers(Range headers) {
86+
void set_headers(Range headers, bool immediate = true) {
8087
if (headers_already_sent)
8188
boost::throw_exception(std::logic_error("Headers have already been sent."));
8289

@@ -99,6 +106,8 @@ namespace boost { namespace network { namespace http {
99106
stream << consts::crlf();
100107
}
101108
stream << consts::crlf();
109+
if (immediate) write_headers_only();
110+
102111
commit = true;
103112
}
104113

@@ -107,19 +116,34 @@ namespace boost { namespace network { namespace http {
107116
}
108117

109118
template <class Range>
110-
void write(Range) {
111-
if (!headers_already_sent) {
112-
// TODO write out the headers that are already
113-
// linearized to the headers_buffer.
114-
}
115-
// linearize the range into a shared array
116-
// schedule a stranded asynchronous write
119+
void write(Range const & range) {
120+
write_impl(
121+
boost::make_iterator_range(range)
122+
, boost::bind(
123+
&async_connection<Tag,Handler>::default_error
124+
, async_connection<Tag,Handler>::shared_from_this()
125+
, _1
126+
)
127+
);
128+
}
129+
130+
template <class Range, class Callback>
131+
void write(Range const & range, Callback const & callback) {
132+
write_impl(
133+
boost::make_iterator_range(range)
134+
, callback
135+
);
117136
}
118137

119138
asio::ip::tcp::socket & socket() { return socket_; }
120139
utils::thread_pool & thread_pool() { return thread_pool_; }
121140

122141
private:
142+
143+
void default_error(boost::system::error_code const & ec) {
144+
// TODO implement a sane default here, for now ignore the error
145+
}
146+
123147
asio::ip::tcp::socket socket_;
124148
asio::io_service::strand strand;
125149
Handler & handler;
@@ -129,6 +153,11 @@ namespace boost { namespace network { namespace http {
129153

130154
typedef boost::array<char, BOOST_NETWORK_HTTP_SERVER_CONNECTION_BUFFER_SIZE>
131155
buffer_type;
156+
typedef boost::array<char, BOOST_NETWORK_HTTP_SERVER_CONNECTION_BUFFER_SIZE>
157+
array;
158+
typedef std::list<shared_ptr<array> > array_list;
159+
typedef boost::shared_ptr<array_list> shared_array_list;
160+
typedef boost::shared_ptr<std::vector<asio::const_buffer> > shared_buffers;
132161
buffer_type read_buffer_;
133162
boost::uint16_t status;
134163

@@ -157,6 +186,106 @@ namespace boost { namespace network { namespace http {
157186
// FIXME -- damn all that work got wiped out because Jeni tripped on the power. :(
158187
}
159188

189+
template <class Range, class Callback>
190+
void write_headers(Range range, Callback callback) {
191+
// TODO send out the headers, then once that's done
192+
// call the write again on the range and callback
193+
}
194+
195+
void write_headers_only() {
196+
}
197+
198+
void handle_write_headers(boost::system::error_code const & ec) {
199+
if (ec) {
200+
// TODO signal somehow that there was an error so that subsequent
201+
// calls to write would throw an exception
202+
return;
203+
}
204+
headers_already_sent = true;
205+
}
206+
207+
void handle_write(
208+
boost::function<void(boost::system::error_code const &)> callback
209+
, shared_array_list temporaries
210+
, shared_buffers buffers
211+
, boost::system::error_code const & ec
212+
) {
213+
// we want to forget the temporaries and buffers
214+
thread_pool().post(boost::bind(callback, ec));
215+
}
216+
217+
template <class Range, class Callback>
218+
void write_impl(Range range, Callback callback) {
219+
if (!headers_already_sent) {
220+
write_headers(range, callback);
221+
return;
222+
}
223+
224+
// linearize the whole range into a vector
225+
// of fixed-sized buffers, then schedule an asynchronous
226+
// write of these buffers -- make sure they are live
227+
// by making these linearized buffers shared and made
228+
// part of the completion handler.
229+
//
230+
// once the range has been linearized and sent, schedule
231+
// a wrapper to be called in the io_service's thread, that
232+
// will re-schedule the given callback into the thread pool
233+
// referred to here so that the io_service's thread can concentrate
234+
// on doing I/O.
235+
//
236+
237+
shared_array_list temporaries =
238+
boost::make_shared<array_list>();
239+
shared_buffers buffers =
240+
boost::make_shared<std::vector<asio::const_buffer> >(0);
241+
242+
std::size_t range_size = boost::distance(range);
243+
buffers->resize(
244+
(range_size / BOOST_NETWORK_HTTP_SERVER_CONNECTION_BUFFER_SIZE)
245+
+ (range_size % BOOST_NETWORK_HTTP_SERVER_CONNECTION_BUFFER_SIZE)
246+
);
247+
std::size_t slice_size =
248+
std::min(
249+
range_size,
250+
BOOST_NETWORK_HTTP_SERVER_CONNECTION_BUFFER_SIZE
251+
);
252+
typename boost::range_iterator<Range>::type
253+
start = boost::begin(range)
254+
, end = boost::end(range);
255+
while (slice_size != 0) {
256+
using boost::adaptors::sliced;
257+
shared_ptr<array> new_array = make_shared<array>();
258+
boost::copy(
259+
range | sliced(0,slice_size)
260+
, new_array->begin()
261+
);
262+
temporaries->push_back(new_array);
263+
buffers->push_back(asio::buffer(new_array->data(), slice_size));
264+
std::advance(start, slice_size);
265+
range = boost::make_iterator_range(start, end);
266+
range_size = boost::distance(range);
267+
slice_size = std::min(range_size, BOOST_NETWORK_HTTP_SERVER_CONNECTION_BUFFER_SIZE);
268+
}
269+
270+
if (!buffers->empty()) {
271+
boost::function<void(boost::system::error_code const &)> f = callback;
272+
asio::async_write(
273+
socket_
274+
, *buffers
275+
, strand.wrap(
276+
boost::bind(
277+
&async_connection<Tag,Handler>::handle_write
278+
, async_connection<Tag,Handler>::shared_from_this()
279+
, f
280+
, temporaries
281+
, buffers // keep these alive until the handler is called!
282+
, boost::asio::placeholders::error
283+
)
284+
)
285+
);
286+
}
287+
}
288+
160289
};
161290

162291
} /* http */

boost/network/protocol/http/server/sync_connection.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
#define BOOST_NETWORK_HTTP_SERVER_SYNC_CONNECTION_HPP_
1010

1111
#ifndef BOOST_NETWORK_HTTP_SERVER_CONNECTION_BUFFER_SIZE
12-
#define BOOST_NETWORK_HTTP_SERVER_CONNECTION_BUFFER_SIZE 1024
12+
#define BOOST_NETWORK_HTTP_SERVER_CONNECTION_BUFFER_SIZE 1024uL
1313
#endif
1414

1515
#include <boost/enable_shared_from_this.hpp>

0 commit comments

Comments
 (0)