Skip to content

Commit 3f106d1

Browse files
committed
More fleshing out of HTTP Async Connection
This commit actually builds in Clang, but doesn't do much (yet). What still needs to be done is to: - Implement the writing of the status, status message, version, and header information especially when a write is first scheduled. - Implement a means for the handler to read asynchronously from the connection provided. - Write up a fairly complex example of how to use the asynchronous HTTP server-side handler More work has to be done in regard to testing with GCC 4.4 and 4.5 as far as builds are concerned.
1 parent 36eb5a7 commit 3f106d1

File tree

7 files changed

+195
-25
lines changed

7 files changed

+195
-25
lines changed

boost/network/protocol/http/header.hpp

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,17 @@
1414
#define HTTP_SERVER3_HEADER_HPP
1515

1616
#include <boost/network/traits/string.hpp>
17+
#include <boost/fusion/adapted/struct/adapt_struct.hpp>
18+
#include <boost/fusion/include/adapt_struct.hpp>
19+
#include <boost/assign/list_of.hpp>
1720

1821
namespace boost { namespace network { namespace http {
1922

2023
template <class Tag>
2124
struct request_header
2225
{
2326
typedef typename string<Tag>::type string_type;
24-
string_type name;
25-
std::string value;
27+
string_type name, value;
2628
};
2729

2830
template <class Tag>
@@ -37,4 +39,11 @@ namespace boost { namespace network { namespace http {
3739

3840
} // namespace boost
3941

42+
BOOST_FUSION_ADAPT_TPL_STRUCT(
43+
(Tag),
44+
(boost::network::http::request_header)(Tag),
45+
(typename boost::network::string<Tag>::type, name)
46+
(typename boost::network::string<Tag>::type, value)
47+
)
48+
4049
#endif // HTTP_SERVER3_HEADER_HPP

boost/network/protocol/http/request_parser.hpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,6 @@ class basic_request_parser
9797
} state_;
9898
};
9999

100-
typedef basic_request_parser<tags::http_server> request_parser;
101-
102100
} // namespace http
103101

104102
} // namespace network

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

Lines changed: 164 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@
1818
#include <boost/asio/strand.hpp>
1919
#include <boost/asio/buffer.hpp>
2020
#include <boost/make_shared.hpp>
21+
#include <boost/network/protocol/http/server/request_parser.hpp>
22+
#include <boost/range/iterator_range.hpp>
23+
#include <boost/spirit/include/qi.hpp>
2124
#include <list>
2225
#include <vector>
2326
#include <iterator>
@@ -37,7 +40,6 @@ namespace boost { namespace network { namespace http {
3740

3841
template <class Tag, class Handler>
3942
struct async_connection : boost::enable_shared_from_this<async_connection<Tag,Handler> > {
40-
static std::size_t const connection_buffer_size = BOOST_NETWORK_HTTP_SERVER_CONNECTION_BUFFER_SIZE;
4143

4244
enum status_t {
4345
ok = 200
@@ -61,6 +63,7 @@ namespace boost { namespace network { namespace http {
6163
};
6264

6365
typedef typename string<Tag>::type string_type;
66+
typedef basic_request<Tag> request;
6467

6568
async_connection(
6669
asio::io_service & io_service
@@ -73,7 +76,9 @@ namespace boost { namespace network { namespace http {
7376
, thread_pool_(thread_pool)
7477
, headers_already_sent(false)
7578
, headers_buffer(BOOST_NETWORK_HTTP_SERVER_CONNECTION_HEADER_BUFFER_MAX_SIZE)
76-
{}
79+
{
80+
new_start = read_buffer_.begin();
81+
}
7782

7883
/** Function: template <class Range> set_headers(Range headers)
7984
* Precondition: headers have not been sent yet
@@ -146,22 +151,26 @@ namespace boost { namespace network { namespace http {
146151
// TODO implement a sane default here, for now ignore the error
147152
}
148153

154+
typedef boost::array<char, BOOST_NETWORK_HTTP_SERVER_CONNECTION_BUFFER_SIZE> buffer_type;
155+
typedef boost::array<char, BOOST_NETWORK_HTTP_SERVER_CONNECTION_BUFFER_SIZE> array;
156+
typedef std::list<shared_ptr<array> > array_list;
157+
typedef boost::shared_ptr<array_list> shared_array_list;
158+
typedef boost::shared_ptr<std::vector<asio::const_buffer> > shared_buffers;
159+
typedef request_parser<Tag> request_parser_type;
160+
149161
asio::ip::tcp::socket socket_;
150162
asio::io_service::strand strand;
151163
Handler & handler;
152164
utils::thread_pool & thread_pool_;
153165
bool headers_already_sent;
154166
asio::streambuf headers_buffer;
155167

156-
typedef boost::array<char, connection_buffer_size>
157-
buffer_type;
158-
typedef boost::array<char, connection_buffer_size>
159-
array;
160-
typedef std::list<shared_ptr<array> > array_list;
161-
typedef boost::shared_ptr<array_list> shared_array_list;
162-
typedef boost::shared_ptr<std::vector<asio::const_buffer> > shared_buffers;
163168
buffer_type read_buffer_;
164169
boost::uint16_t status;
170+
request_parser_type parser;
171+
request request_;
172+
buffer_type::iterator new_start;
173+
string_type partial_parsed;
165174

166175
template <class, class> friend struct async_server_base;
167176

@@ -170,24 +179,166 @@ namespace boost { namespace network { namespace http {
170179
};
171180

172181
void start() {
182+
read_more(method);
183+
}
184+
185+
void read_more(state_t state) {
173186
socket_.async_read_some(
174187
asio::buffer(read_buffer_)
175188
, strand.wrap(
176189
boost::bind(
177190
&async_connection<Tag,Handler>::handle_read_data,
178191
async_connection<Tag,Handler>::shared_from_this(),
179-
method,
192+
state,
180193
boost::asio::placeholders::error,
181194
boost::asio::placeholders::bytes_transferred
182195
)
183196
)
184197
);
185198
}
186199

187-
void handle_read_data(state_t, boost::system::error_code const & ec, std::size_t bytes_transferred) {
188-
// FIXME -- damn all that work got wiped out because Jeni tripped on the power. :(
200+
void handle_read_data(state_t state, boost::system::error_code const & ec, std::size_t bytes_transferred) {
201+
if (!ec) {
202+
logic::tribool parsed_ok;
203+
iterator_range<buffer_type::iterator> result_range, input_range;
204+
switch (state) {
205+
case method:
206+
input_range = boost::make_iterator_range(
207+
new_start, read_buffer_.end());
208+
fusion::tie(parsed_ok, result_range) = parser.parse_until(
209+
request_parser_type::method_done, input_range);
210+
if (!parsed_ok) {
211+
client_error();
212+
break;
213+
} else if (parsed_ok == true) {
214+
swap(partial_parsed, request_.method);
215+
request_.method.append(
216+
boost::begin(result_range),
217+
boost::end(result_range));
218+
trim(request_.method);
219+
new_start = boost::end(result_range);
220+
} else {
221+
partial_parsed.append(
222+
boost::begin(result_range),
223+
boost::end(result_range));
224+
new_start = read_buffer_.begin();
225+
read_more(method);
226+
break;
227+
}
228+
case uri:
229+
input_range = boost::make_iterator_range(
230+
new_start, read_buffer_.end());
231+
fusion::tie(parsed_ok, result_range) = parser.parse_until(
232+
request_parser_type::uri_done,
233+
input_range);
234+
if (!parsed_ok) {
235+
client_error();
236+
break;
237+
} else if (parsed_ok == true) {
238+
swap(partial_parsed, request_.destination);
239+
request_.destination.append(
240+
boost::begin(result_range),
241+
boost::end(result_range));
242+
trim(request_.destination);
243+
new_start = boost::end(result_range);
244+
} else {
245+
partial_parsed.append(
246+
boost::begin(result_range),
247+
boost::end(result_range));
248+
new_start = read_buffer_.begin();
249+
read_more(uri);
250+
break;
251+
}
252+
case version:
253+
input_range = boost::make_iterator_range(
254+
new_start, read_buffer_.end());
255+
fusion::tie(parsed_ok, result_range) = parser.parse_until(
256+
request_parser_type::version_done,
257+
input_range);
258+
if (!parsed_ok) {
259+
client_error();
260+
break;
261+
} else if (parsed_ok == true) {
262+
fusion::tuple<uint8_t, uint8_t> version_pair;
263+
using namespace boost::spirit::qi;
264+
partial_parsed.append(boost::begin(result_range), boost::end(result_range));
265+
parse(
266+
partial_parsed.begin(), partial_parsed.end(),
267+
(
268+
lit("HTTP/")
269+
>> ushort_
270+
>> '.'
271+
>> ushort_
272+
)
273+
, version_pair);
274+
request_.http_version_major = fusion::get<0>(version_pair);
275+
request_.http_version_minor = fusion::get<1>(version_pair);
276+
new_start = boost::end(result_range);
277+
} else {
278+
partial_parsed.append(
279+
boost::begin(result_range),
280+
boost::end(result_range));
281+
new_start = read_buffer_.begin();
282+
read_more(version);
283+
break;
284+
}
285+
case headers:
286+
input_range = boost::make_iterator_range(
287+
new_start, read_buffer_.end());
288+
fusion::tie(parsed_ok, result_range) = parser.parse_until(
289+
request_parser_type::headers_done,
290+
input_range);
291+
if (!parsed_ok) {
292+
client_error();
293+
break;
294+
} else if (parsed_ok == true) {
295+
partial_parsed.append(
296+
boost::begin(result_range),
297+
boost::end(result_range));
298+
trim(partial_parsed);
299+
parse_headers(partial_parsed, request_.headers);
300+
new_start = boost::end(result_range);
301+
thread_pool().post(
302+
boost::bind(
303+
&Handler::operator(),
304+
handler,
305+
cref(request_),
306+
async_connection<Tag,Handler>::shared_from_this()));
307+
return;
308+
} else {
309+
partial_parsed.append(
310+
boost::begin(result_range),
311+
boost::end(result_range));
312+
new_start = read_buffer_.begin();
313+
read_more(headers);
314+
break;
315+
}
316+
default:
317+
BOOST_ASSERT(false && "This is a bug, report to the cpp-netlib devel mailing list!");
318+
std::abort();
319+
}
320+
}
321+
// TODO log the error
189322
}
190323

324+
void client_error() {
325+
//FIXME write out a client request error
326+
}
327+
328+
void parse_headers(string_type & input, typename request::headers_container_type & container) {
329+
using namespace boost::spirit::qi;
330+
std::vector<fusion::tuple<std::string,std::string> > headers;
331+
parse(
332+
input.begin(), input.end(),
333+
*(
334+
+(alnum|(punct-':'))
335+
>> lit(": ")
336+
>> +(alnum|space|punct)
337+
>> lit("\r\n")
338+
)
339+
, headers
340+
);
341+
}
191342
template <class Range, class Callback>
192343
void write_headers(Range range, Callback callback) {
193344
// TODO send out the headers, then once that's done
@@ -236,6 +387,7 @@ namespace boost { namespace network { namespace http {
236387
// on doing I/O.
237388
//
238389

390+
static std::size_t const connection_buffer_size = BOOST_NETWORK_HTTP_SERVER_CONNECTION_BUFFER_SIZE;
239391
shared_array_list temporaries =
240392
boost::make_shared<array_list>();
241393
shared_buffers buffers =

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,10 @@ namespace boost { namespace network { namespace http {
1616
struct async_server_base {
1717
typedef basic_request<Tag> request;
1818
typedef basic_response<Tag> response;
19-
typedef async_connection<Tag, Handler> connection;
2019
typedef response_header<Tag> response_header;
2120
typedef typename string<Tag>::type string_type;
21+
typedef async_connection<Tag,Handler> connection;
22+
typedef shared_ptr<connection> connection_ptr;
2223

2324
async_server_base(string_type const & address
2425
, string_type const & port
@@ -62,7 +63,7 @@ namespace boost { namespace network { namespace http {
6263
asio::io_service io_service;
6364
asio::ip::tcp::acceptor acceptor;
6465
bool stopping;
65-
boost::shared_ptr<async_connection<Tag,Handler> > new_connection;
66+
connection_ptr new_connection;
6667

6768
void handle_accept(boost::system::error_code const & ec) {
6869
if (!ec) {

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

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
// http://www.boost.org/LICENSE_1_0.txt)
88

99
#include <boost/network/traits/string.hpp>
10+
#include <boost/fusion/adapted/struct/adapt_struct.hpp>
11+
#include <boost/fusion/include/adapt_struct.hpp>
12+
#include <boost/assign/list_of.hpp>
1013
#include <algorithm>
1114

1215
namespace boost { namespace network { namespace http {
@@ -17,7 +20,6 @@ namespace boost { namespace network { namespace http {
1720
string_type name, value;
1821
};
1922

20-
2123
template <class Tag>
2224
void swap(response_header<Tag> & l, response_header<Tag> & r) {
2325
std::swap(l.name, r.name);
@@ -30,4 +32,11 @@ namespace boost { namespace network { namespace http {
3032

3133
} /* boost */
3234

35+
BOOST_FUSION_ADAPT_TPL_STRUCT(
36+
(Tag),
37+
(boost::network::http::response_header) (Tag),
38+
(typename boost::network::string<Tag>::type, name)
39+
(typename boost::network::string<Tag>::type, value)
40+
)
41+
3342
#endif /* BOOST_NETWORK_PROTOCOL_HTTP_SERVER_HEADER_HPP_20101027 */

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,7 @@ namespace boost { namespace network { namespace http {
260260

261261
typedef boost::array<char,BOOST_NETWORK_HTTP_SERVER_CONNECTION_BUFFER_SIZE> buffer_type;
262262
buffer_type buffer_;
263+
typedef basic_request_parser<Tag> request_parser;
263264
request_parser parser_;
264265
basic_request<Tag> request_;
265266
basic_response<Tag> response_;

libs/network/test/http_async_server.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,18 @@ struct async_hello_world;
1717
typedef http::async_server<async_hello_world> server;
1818

1919
struct async_hello_world {
20-
void operator()(server::request const & request, server::connection & connection) {
20+
void operator()(server::request const & request, server::connection_ptr connection) {
2121
server::response_header headers[] = {
2222
{"Connection", "close"}
2323
, {"Content-Type", "text/plain"}
2424
};
25-
connection.set_headers(boost::make_iterator_range(headers, headers+2));
25+
connection->set_headers(boost::make_iterator_range(headers, headers+2));
2626
if (request.method != "GET") {
27-
connection.set_status(server::connection::not_supported);
28-
connection.write("Unsupported method.");
27+
connection->set_status(server::connection::not_supported);
28+
connection->write("Unsupported method.");
2929
} else {
30-
connection.set_status(server::connection::ok);
31-
connection.write("Hello, World!");
30+
connection->set_status(server::connection::ok);
31+
connection->write("Hello, World!");
3232
}
3333
}
3434
};

0 commit comments

Comments
 (0)