1 #ifndef BOOST_NETWORK_PROTOCOL_HTTP_PARSER_INCREMENTAL_HPP_20100909
2 #define BOOST_NETWORK_PROTOCOL_HTTP_PARSER_INCREMENTAL_HPP_20100909
3 
4 // Copyright Dean Michael Berris 2010.
5 // Copyright 2011 Dean Michael Berris (dberris@google.com).
6 // Copyright 2011 Google, Inc.
7 // Distributed under the Boost Software License, Version 1.0.
8 // (See accompanying file LICENSE_1_0.txt or copy at
9 // http://www.boost.org/LICENSE_1_0.txt)
10 
11 #include <iterator>
12 #include <tuple>
13 #include <boost/algorithm/string/classification.hpp>
14 #include <boost/logic/tribool.hpp>
15 #include <boost/network/tags.hpp>
16 #include <boost/network/traits/string.hpp>
17 #include <boost/range.hpp>
18 #include <utility>
19 
20 namespace boost {
21 namespace network {
22 namespace http {
23 
24 template <class Tag>
25 struct response_parser {
26 
27   enum state_t {
28     http_response_begin,
29     http_version_h,
30     http_version_t1,
31     http_version_t2,
32     http_version_p,
33     http_version_slash,
34     http_version_major,
35     http_version_dot,
36     http_version_minor,
37     http_version_done,
38     http_status_digit,
39     http_status_done,
40     http_status_message_char,
41     http_status_message_cr,
42     http_status_message_done,
43     http_header_name_char,
44     http_header_colon,
45     http_header_value_char,
46     http_header_line_cr,
47     http_header_line_done,
48     http_headers_end_cr,
49     http_headers_done
50   };
51 
response_parserboost::network::http::response_parser52   explicit response_parser(state_t state = http_response_begin)
53       : state_(state) {}
54 
response_parserboost::network::http::response_parser55   response_parser(response_parser const& other) : state_(other.state_) {}
56 
57   ~response_parser() = default;
58 
swapboost::network::http::response_parser59   void swap(response_parser& other) { std::swap(other.state_, this->state_); }
60 
operator =boost::network::http::response_parser61   response_parser& operator=(response_parser rhs) {
62     rhs.swap(*this);
63     return *this;
64   }
65 
66   template <class Range>
67   std::tuple<logic::tribool, iterator_range<typename Range::const_iterator> >
parse_untilboost::network::http::response_parser68   parse_until(state_t stop_state, Range& range_) {
69     logic::tribool parsed_ok(logic::indeterminate);
70     typename Range::const_iterator start = std::begin(range_),
71                                    current = start, end = std::end(range_);
72     boost::iterator_range<typename Range::const_iterator> local_range =
73         boost::make_iterator_range(start, end);
74     while (!boost::empty(local_range) && indeterminate(parsed_ok)) {
75       current = boost::begin(local_range);
76       if (state_ == stop_state) {
77         parsed_ok = true;
78       } else {
79         switch (state_) {
80           case http_response_begin:
81             if (*current == ' ' || *current == '\r' || *current == '\n') {
82               // skip valid leading whitespace
83               ++start;
84               ++current;
85             } else if (*current == 'H') {
86               state_ = http_version_h;
87               start = current;
88               ++current;
89             } else {
90               parsed_ok = false;
91             }
92             break;
93           case http_version_h:
94             if (*current == 'T') {
95               state_ = http_version_t1;
96               ++current;
97             } else {
98               parsed_ok = false;
99             }
100             break;
101           case http_version_t1:
102             if (*current == 'T') {
103               state_ = http_version_t2;
104               ++current;
105             } else {
106               parsed_ok = false;
107             }
108             break;
109           case http_version_t2:
110             if (*current == 'P') {
111               state_ = http_version_p;
112               ++current;
113             } else {
114               parsed_ok = false;
115             }
116             break;
117           case http_version_p:
118             if (*current == '/') {
119               state_ = http_version_slash;
120               ++current;
121             } else {
122               parsed_ok = false;
123             }
124             break;
125           case http_version_slash:
126             if (algorithm::is_digit()(*current)) {
127               state_ = http_version_major;
128               ++current;
129             } else {
130               parsed_ok = false;
131             }
132             break;
133           case http_version_major:
134             if (*current == '.') {
135               state_ = http_version_dot;
136               ++current;
137             } else {
138               parsed_ok = false;
139             }
140             break;
141           case http_version_dot:
142             if (algorithm::is_digit()(*current)) {
143               state_ = http_version_minor;
144               ++current;
145             } else {
146               parsed_ok = false;
147             }
148             break;
149           case http_version_minor:
150             if (*current == ' ') {
151               state_ = http_version_done;
152               ++current;
153             } else {
154               parsed_ok = false;
155             }
156             break;
157           case http_version_done:
158             if (algorithm::is_digit()(*current)) {
159               state_ = http_status_digit;
160               ++current;
161             } else {
162               parsed_ok = false;
163             }
164             break;
165           case http_status_digit:
166             if (algorithm::is_digit()(*current)) {
167               ++current;
168             } else if (*current == ' ') {
169               state_ = http_status_done;
170               ++current;
171             } else if (*current == '\r' || *current == '\n') {
172               state_ = http_status_done;
173             } else {
174               parsed_ok = false;
175             }
176             break;
177           case http_status_done:
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;
185               ++current;
186             } else {
187               state_ = http_status_message_char;
188               ++current;
189             }
190             break;
191           case http_status_message_char:
192             if (*current == '\r') {
193               state_ = http_status_message_cr;
194               ++current;
195             } else if (*current == '\n') {
196               state_ = http_status_message_done;
197               ++current;
198             } else {
199               ++current;
200             }
201             break;
202           case http_status_message_cr:
203             if (*current == '\n') {
204               state_ = http_status_message_done;
205               ++current;
206             } else {
207               parsed_ok = false;
208             }
209             break;
210           case http_status_message_done:
211           case http_header_line_done:
212             if (*current == ' ') {
213               ++current;
214             } else if (algorithm::is_alnum()(*current) ||
215                        algorithm::is_punct()(*current)) {
216               state_ = http_header_name_char;
217               ++current;
218             } else if (*current == '\r') {
219               state_ = http_headers_end_cr;
220               ++current;
221             } else if (*current == '\n') {
222               state_ = http_headers_done;
223               ++current;
224             } else {
225               parsed_ok = false;
226             }
227             break;
228           case http_header_name_char:
229             if (*current == ':') {
230               state_ = http_header_colon;
231               ++current;
232             } else if (*current == '\r') {
233               state_ = http_header_line_cr;
234               ++current;
235             } else if (*current == '\n') {
236               state_ = http_header_line_done;
237               ++current;
238             } else if (algorithm::is_alnum()(*current) ||
239                        algorithm::is_space()(*current) ||
240                        algorithm::is_punct()(*current)) {
241               ++current;
242             } else {
243               parsed_ok = false;
244             }
245             break;
246           case http_header_colon:
247             if (*current == '\r') {
248               state_ = http_header_line_cr;
249               ++current;
250             } else if (*current == '\n') {
251               state_ = http_header_line_done;
252               ++current;
253             } else if (algorithm::is_space()(*current)) {
254               ++current;
255             } else {
256               state_ = http_header_value_char;
257               ++current;
258             }
259             break;
260           case http_header_value_char:
261             if (*current == '\r') {
262               state_ = http_header_line_cr;
263               ++current;
264             } else if (*current == '\n') {
265               state_ = http_header_line_done;
266               ++current;
267             } else {
268               ++current;
269             }
270             break;
271           case http_header_line_cr:
272             if (*current == '\n') {
273               state_ = http_header_line_done;
274               ++current;
275             } else {
276               parsed_ok = false;
277             }
278             break;
279           case http_headers_end_cr:
280             if (*current == '\n') {
281               state_ = http_headers_done;
282               ++current;
283             } else {
284               parsed_ok = false;
285             }
286             break;
287           default:
288             parsed_ok = false;
289         }
290       }
291 
292       local_range = boost::make_iterator_range(current, end);
293     }
294     if (state_ == stop_state) { parsed_ok = true;
295 }
296     return std::make_tuple(parsed_ok,
297                            boost::make_iterator_range(start, current));
298   }
299 
stateboost::network::http::response_parser300   state_t state() { return state_; }
301 
resetboost::network::http::response_parser302   void reset(state_t new_state = http_response_begin) { state_ = new_state; }
303 
304  private:
305   state_t state_;
306 };
307 
308 }  // namespace http
309  /* http */
310 
311 }  // namespace network
312  /* network */
313 
314 }  // namespace boost
315  /* boost */
316 
317 #endif
318