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