Skip to content

Commit f0eaf48

Browse files
committed
Merge pull request cpp-netlib#408 from leecoder/0.11-devel
Fixes cpp-netlib#393: enhancing response incremental parser
2 parents 7af6653 + 80826f0 commit f0eaf48

File tree

3 files changed

+178
-21
lines changed

3 files changed

+178
-21
lines changed

boost/network/protocol/http/parser/incremental.hpp

+33-14
Original file line numberDiff line numberDiff line change
@@ -168,26 +168,35 @@ namespace boost { namespace network { namespace http {
168168
} else if (*current == ' ') {
169169
state_ = http_status_done;
170170
++current;
171+
} else if (*current == '\r' || *current == '\n') {
172+
state_ = http_status_done;
171173
} else {
172174
parsed_ok = false;
173175
}
174176
break;
175177
case http_status_done:
176-
if (algorithm::is_alnum()(*current)) {
177-
state_ = http_status_message_char;
178+
if (*current == ' ') {
179+
++current;
180+
} else if (*current == '\r') {
181+
state_ = http_status_message_cr;
182+
++current;
183+
} else if (*current == '\n') {
184+
state_ = http_status_message_done;
178185
++current;
179186
} else {
180-
parsed_ok = false;
187+
state_ = http_status_message_char;
188+
++current;
181189
}
182190
break;
183191
case http_status_message_char:
184-
if (algorithm::is_alnum()(*current) || algorithm::is_punct()(*current) || (*current == ' ')) {
185-
++current;
186-
} else if (*current == '\r') {
192+
if (*current == '\r') {
187193
state_ = http_status_message_cr;
188194
++current;
195+
} else if (*current == '\n') {
196+
state_ = http_status_message_done;
197+
++current;
189198
} else {
190-
parsed_ok = false;
199+
++current;
191200
}
192201
break;
193202
case http_status_message_cr:
@@ -200,12 +209,17 @@ namespace boost { namespace network { namespace http {
200209
break;
201210
case http_status_message_done:
202211
case http_header_line_done:
203-
if (algorithm::is_alnum()(*current)) {
212+
if (*current == ' ') {
213+
++current;
214+
} else if (algorithm::is_alnum()(*current) || algorithm::is_punct()(*current)) {
204215
state_ = http_header_name_char;
205216
++current;
206217
} else if (*current == '\r') {
207218
state_ = http_headers_end_cr;
208219
++current;
220+
} else if (*current == '\n') {
221+
state_ = http_headers_done;
222+
++current;
209223
} else {
210224
parsed_ok = false;
211225
}
@@ -221,21 +235,26 @@ namespace boost { namespace network { namespace http {
221235
}
222236
break;
223237
case http_header_colon:
224-
if (algorithm::is_space()(*current)) {
238+
if (*current == '\r') {
239+
state_ = http_header_line_cr;
225240
++current;
226-
} else if (algorithm::is_alnum()(*current) || algorithm::is_punct()(*current)) {
227-
state_ = http_header_value_char;
241+
} else if (*current == '\n') {
242+
state_ = http_header_line_done;
243+
++current;
244+
} else if (algorithm::is_space()(*current)) {
228245
++current;
229246
} else {
230-
parsed_ok = false;
247+
state_ = http_header_value_char;
248+
++current;
231249
}
232250
break;
233251
case http_header_value_char:
234252
if (*current == '\r') {
235253
state_ = http_header_line_cr;
236254
++current;
237-
} else if (algorithm::is_cntrl()(*current)) {
238-
parsed_ok = false;
255+
} else if (*current == '\n') {
256+
state_ = http_header_line_done;
257+
++current;
239258
} else {
240259
++current;
241260
}

libs/network/test/http/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ endif()
1313

1414
if (Boost_FOUND)
1515
set ( TESTS
16+
response_incremental_parser_test
1617
request_incremental_parser_test
1718
request_linearize_test
1819
)

libs/network/test/http/response_incremental_parser_test.cpp

+144-7
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,17 @@ namespace logic = boost::logic;
5353
namespace fusion = boost::fusion;
5454
using namespace boost::network::http;
5555

56+
struct crlf {
57+
static const std::string literal;
58+
};
59+
const std::string crlf::literal = "\r\n";
60+
struct lf {
61+
static const std::string literal;
62+
};
63+
const std::string lf::literal = "\n";
64+
typedef boost::mpl::vector<crlf, lf> eol_types;
65+
66+
5667
BOOST_AUTO_TEST_CASE(incremental_parser_constructor) {
5768
response_parser<tags::default_string> p; // default constructible
5869
}
@@ -114,7 +125,7 @@ BOOST_AUTO_TEST_CASE(incremental_parser_parse_http_version) {
114125
* the parser doesn't do any conversions from string to integer
115126
* and outsource that part to the user of the parser.
116127
*/
117-
BOOST_AUTO_TEST_CASE(incremental_parser_parse_status) {
128+
BOOST_AUTO_TEST_CASE_TEMPLATE(incremental_parser_parse_status, eol, eol_types) {
118129
typedef response_parser<tags::default_string> response_parser_type;
119130
typedef boost::iterator_range<std::string::const_iterator> range_type;
120131
// We want to create a parser that has been initialized to a specific
@@ -140,17 +151,25 @@ BOOST_AUTO_TEST_CASE(incremental_parser_parse_status) {
140151
BOOST_CHECK_EQUAL(parsed_ok, false);
141152
parsed = std::string(boost::begin(result_range), boost::end(result_range));
142153
std::cout << "PARSED: " << parsed << " state=" << p.state() << std::endl;
154+
155+
valid_status = "200" + eol::literal;
156+
fusion::tie(parsed_ok, result_range) = p.parse_until(
157+
response_parser_type::http_status_done,
158+
valid_status);
159+
BOOST_CHECK_EQUAL(parsed_ok, true);
160+
parsed = std::string(boost::begin(result_range), boost::end(result_range));
161+
std::cout << "PARSED: " << parsed << " state=" << p.state() << std::endl;
143162
}
144163

145164
/** In this test then we get the rest of the first line of the HTTP
146165
* Response, and treat it as the status message.
147166
*/
148-
BOOST_AUTO_TEST_CASE(incremental_parser_parse_status_message) {
167+
BOOST_AUTO_TEST_CASE_TEMPLATE(incremental_parser_parse_status_message, eol, eol_types) {
149168
typedef response_parser<tags::default_string> response_parser_type;
150169
typedef boost::iterator_range<std::string::const_iterator> range_type;
151170
response_parser_type p(response_parser_type::http_status_done);
152171

153-
std::string valid_status_message = "OK\r\nServer: Foo";
172+
std::string valid_status_message = "OK" + eol::literal + "Server: Foo";
154173
logic::tribool parsed_ok;
155174
range_type result_range;
156175
fusion::tie(parsed_ok, result_range) = p.parse_until(
@@ -161,7 +180,25 @@ BOOST_AUTO_TEST_CASE(incremental_parser_parse_status_message) {
161180
std::cout << "PARSED: " << parsed << " state=" << p.state() << std::endl;
162181

163182
p.reset(response_parser_type::http_status_done);
164-
valid_status_message = "OK\r\n";
183+
valid_status_message = "OK" + eol::literal;
184+
fusion::tie(parsed_ok, result_range) = p.parse_until(
185+
response_parser_type::http_status_message_done,
186+
valid_status_message);
187+
BOOST_CHECK_EQUAL(parsed_ok, true);
188+
parsed = std::string(boost::begin(result_range), boost::end(result_range));
189+
std::cout << "PARSED: " << parsed << " state=" << p.state() << std::endl;
190+
191+
p.reset(response_parser_type::http_status_done);
192+
valid_status_message = "Internal Server Error" + eol::literal;
193+
fusion::tie(parsed_ok, result_range) = p.parse_until(
194+
response_parser_type::http_status_message_done,
195+
valid_status_message);
196+
BOOST_CHECK_EQUAL(parsed_ok, true);
197+
parsed = std::string(boost::begin(result_range), boost::end(result_range));
198+
std::cout << "PARSED: " << parsed << " state=" << p.state() << std::endl;
199+
200+
p.reset(response_parser_type::http_status_done);
201+
valid_status_message = eol::literal;
165202
fusion::tie(parsed_ok, result_range) = p.parse_until(
166203
response_parser_type::http_status_message_done,
167204
valid_status_message);
@@ -170,7 +207,7 @@ BOOST_AUTO_TEST_CASE(incremental_parser_parse_status_message) {
170207
std::cout << "PARSED: " << parsed << " state=" << p.state() << std::endl;
171208

172209
p.reset(response_parser_type::http_status_done);
173-
valid_status_message = "Internal Server Error\r\n";
210+
valid_status_message = "한글메시지" + eol::literal;
174211
fusion::tie(parsed_ok, result_range) = p.parse_until(
175212
response_parser_type::http_status_message_done,
176213
valid_status_message);
@@ -181,12 +218,12 @@ BOOST_AUTO_TEST_CASE(incremental_parser_parse_status_message) {
181218

182219
/** This test specifices how one-line-per-header parsing happens incrementally.
183220
*/
184-
BOOST_AUTO_TEST_CASE(incremental_parser_parse_header_lines) {
221+
BOOST_AUTO_TEST_CASE_TEMPLATE(incremental_parser_parse_header_lines, eol, eol_types) {
185222
typedef response_parser<tags::default_string> response_parser_type;
186223
typedef boost::iterator_range<std::string::const_iterator> range_type;
187224
response_parser_type p(response_parser_type::http_status_message_done);
188225

189-
std::string valid_headers = "Server: Foo\r\nContent-Type: application/json\r\n\r\n";
226+
std::string valid_headers = "Server: Foo" + eol::literal + "Content-Type: application/json" + eol::literal + eol::literal;
190227
logic::tribool parsed_ok;
191228
range_type result_range;
192229
fusion::tie(parsed_ok, result_range) = p.parse_until(
@@ -211,5 +248,105 @@ BOOST_AUTO_TEST_CASE(incremental_parser_parse_header_lines) {
211248
valid_headers);
212249
BOOST_CHECK_EQUAL(parsed_ok, true);
213250
BOOST_CHECK(parsed1 != parsed2);
251+
252+
p.reset(response_parser_type::http_status_message_done);
253+
valid_headers = " Server: Foo" + eol::literal + " Content-Type: application/json" + eol::literal + eol::literal;
254+
fusion::tie(parsed_ok, result_range) = p.parse_until(
255+
response_parser_type::http_header_line_done,
256+
valid_headers);
257+
BOOST_CHECK_EQUAL(parsed_ok, true);
258+
parsed1 = std::string(boost::begin(result_range), boost::end(result_range));
259+
std::cout << "PARSED: " << parsed1 << " state=" << p.state() << std::endl;
260+
p.reset(response_parser_type::http_status_message_done);
261+
end = valid_headers.end();
262+
valid_headers.assign(boost::end(result_range), end);
263+
fusion::tie(parsed_ok, result_range) = p.parse_until(
264+
response_parser_type::http_header_line_done,
265+
valid_headers);
266+
BOOST_CHECK_EQUAL(parsed_ok, true);
267+
parsed2 = std::string(boost::begin(result_range), boost::end(result_range));
268+
std::cout << "PARSED: " << parsed2 << " state=" << p.state() << std::endl;
269+
valid_headers.assign(boost::end(result_range), end);
270+
p.reset(response_parser_type::http_status_message_done);
271+
fusion::tie(parsed_ok, result_range) = p.parse_until(
272+
response_parser_type::http_headers_done,
273+
valid_headers);
274+
BOOST_CHECK_EQUAL(parsed_ok, true);
275+
BOOST_CHECK(parsed1 != parsed2);
276+
277+
p.reset(response_parser_type::http_status_message_done);
278+
valid_headers = "_Server: Foo" + eol::literal + "_Content-Type: application/json" + eol::literal + eol::literal;
279+
fusion::tie(parsed_ok, result_range) = p.parse_until(
280+
response_parser_type::http_header_line_done,
281+
valid_headers);
282+
BOOST_CHECK_EQUAL(parsed_ok, true);
283+
parsed1 = std::string(boost::begin(result_range), boost::end(result_range));
284+
std::cout << "PARSED: " << parsed1 << " state=" << p.state() << std::endl;
285+
p.reset(response_parser_type::http_status_message_done);
286+
end = valid_headers.end();
287+
valid_headers.assign(boost::end(result_range), end);
288+
fusion::tie(parsed_ok, result_range) = p.parse_until(
289+
response_parser_type::http_header_line_done,
290+
valid_headers);
291+
BOOST_CHECK_EQUAL(parsed_ok, true);
292+
parsed2 = std::string(boost::begin(result_range), boost::end(result_range));
293+
std::cout << "PARSED: " << parsed2 << " state=" << p.state() << std::endl;
294+
valid_headers.assign(boost::end(result_range), end);
295+
p.reset(response_parser_type::http_status_message_done);
296+
fusion::tie(parsed_ok, result_range) = p.parse_until(
297+
response_parser_type::http_headers_done,
298+
valid_headers);
299+
BOOST_CHECK_EQUAL(parsed_ok, true);
300+
BOOST_CHECK(parsed1 != parsed2);
301+
302+
p.reset(response_parser_type::http_status_message_done);
303+
valid_headers = "Server: " + eol::literal + "Content-Type: application/json" + eol::literal + eol::literal;
304+
fusion::tie(parsed_ok, result_range) = p.parse_until(
305+
response_parser_type::http_header_line_done,
306+
valid_headers);
307+
BOOST_CHECK_EQUAL(parsed_ok, true);
308+
parsed1 = std::string(boost::begin(result_range), boost::end(result_range));
309+
std::cout << "PARSED: " << parsed1 << " state=" << p.state() << std::endl;
310+
p.reset(response_parser_type::http_status_message_done);
311+
end = valid_headers.end();
312+
valid_headers.assign(boost::end(result_range), end);
313+
fusion::tie(parsed_ok, result_range) = p.parse_until(
314+
response_parser_type::http_header_line_done,
315+
valid_headers);
316+
BOOST_CHECK_EQUAL(parsed_ok, true);
317+
parsed2 = std::string(boost::begin(result_range), boost::end(result_range));
318+
std::cout << "PARSED: " << parsed2 << " state=" << p.state() << std::endl;
319+
valid_headers.assign(boost::end(result_range), end);
320+
p.reset(response_parser_type::http_status_message_done);
321+
fusion::tie(parsed_ok, result_range) = p.parse_until(
322+
response_parser_type::http_headers_done,
323+
valid_headers);
324+
BOOST_CHECK_EQUAL(parsed_ok, true);
325+
BOOST_CHECK(parsed1 != parsed2);
326+
327+
p.reset(response_parser_type::http_status_message_done);
328+
valid_headers = "Server: 서버" + eol::literal + "Content-Type: application/json" + eol::literal + eol::literal;
329+
fusion::tie(parsed_ok, result_range) = p.parse_until(
330+
response_parser_type::http_header_line_done,
331+
valid_headers);
332+
BOOST_CHECK_EQUAL(parsed_ok, true);
333+
parsed1 = std::string(boost::begin(result_range), boost::end(result_range));
334+
std::cout << "PARSED: " << parsed1 << " state=" << p.state() << std::endl;
335+
p.reset(response_parser_type::http_status_message_done);
336+
end = valid_headers.end();
337+
valid_headers.assign(boost::end(result_range), end);
338+
fusion::tie(parsed_ok, result_range) = p.parse_until(
339+
response_parser_type::http_header_line_done,
340+
valid_headers);
341+
BOOST_CHECK_EQUAL(parsed_ok, true);
342+
parsed2 = std::string(boost::begin(result_range), boost::end(result_range));
343+
std::cout << "PARSED: " << parsed2 << " state=" << p.state() << std::endl;
344+
valid_headers.assign(boost::end(result_range), end);
345+
p.reset(response_parser_type::http_status_message_done);
346+
fusion::tie(parsed_ok, result_range) = p.parse_until(
347+
response_parser_type::http_headers_done,
348+
valid_headers);
349+
BOOST_CHECK_EQUAL(parsed_ok, true);
350+
BOOST_CHECK(parsed1 != parsed2);
214351
}
215352

0 commit comments

Comments
 (0)