Line data Source code
1 : // 2 : // Copyright (c) 2021 Vinnie Falco (vinnie.falco@gmail.com) 3 : // 4 : // Distributed under the Boost Software License, Version 1.0. (See accompanying 5 : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 6 : // 7 : // Official repository: https://github.com/cppalliance/http_proto 8 : // 9 : 10 : #include <boost/http_proto/rfc/detail/rules.hpp> 11 : #include <boost/url/grammar/digit_chars.hpp> 12 : 13 : namespace boost { 14 : namespace http_proto { 15 : namespace detail { 16 : 17 : auto 18 4912 : crlf_rule_t:: 19 : parse( 20 : char const*& it, 21 : char const* end) const noexcept -> 22 : system::result<value_type> 23 : { 24 4912 : if(it == end) 25 529 : return grammar::error::need_more; 26 4383 : if(*it != '\r') 27 0 : return grammar::error::mismatch; 28 4383 : ++it; 29 4383 : if(it == end) 30 219 : return grammar::error::need_more; 31 4164 : if(*it != '\n') 32 0 : return grammar::error::mismatch; 33 4164 : ++it; 34 4164 : return {}; 35 : } 36 : 37 : //------------------------------------------------ 38 : 39 : auto 40 2904 : version_rule_t:: 41 : parse( 42 : char const*& it, 43 : char const* end) const noexcept -> 44 : system::result<value_type> 45 : { 46 2904 : value_type v = 0; 47 2904 : if(it == end) 48 : { 49 : // expected "HTTP/" 50 135 : BOOST_HTTP_PROTO_RETURN_EC( 51 : grammar::error::need_more); 52 : } 53 2769 : if(end - it >= 5) 54 : { 55 2346 : if(std::memcmp( 56 : it, "HTTP/", 5) != 0) 57 : { 58 0 : BOOST_HTTP_PROTO_RETURN_EC( 59 : grammar::error::mismatch); 60 : } 61 2346 : it += 5; 62 : } 63 2769 : if(it == end) 64 : { 65 : // expected DIGIT 66 72 : BOOST_HTTP_PROTO_RETURN_EC( 67 : grammar::error::need_more); 68 : } 69 2697 : if(! grammar::digit_chars(*it)) 70 : { 71 : // expected DIGIT 72 423 : BOOST_HTTP_PROTO_RETURN_EC( 73 : grammar::error::need_more); 74 : } 75 2274 : v = 10 * (*it++ - '0'); 76 2274 : if(it == end) 77 : { 78 : // expected "." 79 180 : BOOST_HTTP_PROTO_RETURN_EC( 80 : grammar::error::need_more); 81 : } 82 2094 : if(*it != '.') 83 : { 84 : // expected "." 85 0 : BOOST_HTTP_PROTO_RETURN_EC( 86 : grammar::error::need_more); 87 : } 88 2094 : ++it; 89 2094 : if(it == end) 90 : { 91 : // expected DIGIT 92 71 : BOOST_HTTP_PROTO_RETURN_EC( 93 : grammar::error::need_more); 94 : } 95 2023 : if(! grammar::digit_chars(*it)) 96 : { 97 : // expected DIGIT 98 0 : BOOST_HTTP_PROTO_RETURN_EC( 99 : grammar::error::need_more); 100 : } 101 2023 : v += *it++ - '0'; 102 2023 : return v; 103 : } 104 : 105 : //------------------------------------------------ 106 : 107 : auto 108 500 : status_code_rule_t:: 109 : parse( 110 : char const*& it, 111 : char const* end) const noexcept -> 112 : system::result<value_type> 113 : { 114 : auto const dig = 115 1449 : [](char c) -> int 116 : { 117 1449 : unsigned char uc(c - '0'); 118 1449 : if(uc > 9) 119 0 : return -1; 120 1449 : return uc; 121 : }; 122 : 123 500 : if(it == end) 124 : { 125 : // end 126 9 : BOOST_HTTP_PROTO_RETURN_EC( 127 : grammar::error::need_more); 128 : } 129 491 : auto it0 = it; 130 491 : int v = dig(*it); 131 491 : if(v == -1) 132 : { 133 : // expected DIGIT 134 0 : BOOST_HTTP_PROTO_RETURN_EC( 135 : grammar::error::mismatch); 136 : } 137 491 : value_type t; 138 491 : t.v = 100 * v; 139 491 : ++it; 140 491 : if(it == end) 141 : { 142 : // end 143 8 : BOOST_HTTP_PROTO_RETURN_EC( 144 : grammar::error::need_more); 145 : } 146 483 : v = dig(*it); 147 483 : if(v == -1) 148 : { 149 : // expected DIGIT 150 0 : BOOST_HTTP_PROTO_RETURN_EC( 151 : grammar::error::mismatch); 152 : } 153 483 : t.v = t.v + (10 * v); 154 483 : ++it; 155 483 : if(it == end) 156 : { 157 : // end 158 8 : BOOST_HTTP_PROTO_RETURN_EC( 159 : grammar::error::need_more); 160 : } 161 475 : v = dig(*it); 162 475 : if(v == -1) 163 : { 164 : // expected DIGIT 165 0 : BOOST_HTTP_PROTO_RETURN_EC( 166 : grammar::error::need_more); 167 : } 168 475 : t.v = t.v + v; 169 475 : ++it; 170 : 171 475 : t.s = core::string_view(it0, it - it0); 172 475 : t.st = int_to_status(t.v); 173 475 : return t; 174 : } 175 : 176 : //------------------------------------------------ 177 : 178 : auto 179 5837 : field_rule_t:: 180 : parse( 181 : char const*& it, 182 : char const* end) const noexcept -> 183 : system::result<value_type> 184 : { 185 5837 : if(it == end) 186 : { 187 152 : BOOST_HTTP_PROTO_RETURN_EC( 188 : grammar::error::need_more); 189 : } 190 : // check for leading CRLF 191 5685 : if(it[0] == '\r') 192 : { 193 1898 : ++it; 194 1898 : if(it == end) 195 : { 196 112 : BOOST_HTTP_PROTO_RETURN_EC( 197 : grammar::error::need_more); 198 : } 199 1786 : if(*it != '\n') 200 : { 201 0 : BOOST_HTTP_PROTO_RETURN_EC( 202 : grammar::error::mismatch); 203 : } 204 : // end of fields 205 1786 : ++it; 206 1786 : BOOST_HTTP_PROTO_RETURN_EC( 207 : grammar::error::end_of_range); 208 : } 209 : 210 3787 : value_type v; 211 : 212 : // field name 213 : { 214 : auto rv = grammar::parse( 215 3787 : it, end, grammar::tuple_rule( 216 : token_rule, 217 3787 : grammar::squelch( 218 3787 : grammar::delim_rule(':')))); 219 3787 : if(! rv) 220 397 : return rv.error(); 221 3390 : v.name = rv.value(); 222 : } 223 : 224 : // consume all obs-fold until field char or end of field: 225 : // 226 : // HTTP-message = start-line *( header-field CRLF ) CRLF [ message-body ] 227 : // header-field = field-name ":" OWS field-value OWS 228 : // field-value = *( field-content / obs-fold ) 229 : // field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ] 230 : // obs-fold = CRLF 1*( SP / HTAB ) 231 : // 232 : for(;;) 233 : { 234 3684 : skip_ows(it, end); 235 3684 : if(it == end) 236 : { 237 227 : BOOST_HTTP_PROTO_RETURN_EC( 238 : grammar::error::need_more); 239 : } 240 3457 : if(*it != '\r') 241 : { 242 : // start of value 243 2897 : break; 244 : } 245 560 : ++it; 246 560 : if(it == end) 247 : { 248 60 : BOOST_HTTP_PROTO_RETURN_EC( 249 : grammar::error::need_more); 250 : } 251 500 : if(*it != '\n') 252 : { 253 0 : BOOST_HTTP_PROTO_RETURN_EC( 254 : grammar::error::mismatch); 255 : } 256 500 : ++it; 257 500 : if(it == end) 258 : { 259 56 : BOOST_HTTP_PROTO_RETURN_EC( 260 : grammar::error::need_more); 261 : } 262 : // FIXME: this should be a loop of some kind as we've detected a valid 263 : // CRLF at this stage but need to account for the ABNF specifying: 264 : // obs-fold = CRLF 1*( SP / HTAB ) 265 444 : if(*it != ' ' && 266 150 : *it != '\t') 267 : { 268 : // because we saw a CRLF and didn't see the required SP / HTAB, 269 : // we know we have a zero length field value 270 150 : v.value = core::string_view(it, 0); 271 150 : return v; 272 : } 273 : // eat obs-fold 274 294 : ++it; 275 294 : v.has_obs_fold = true; 276 294 : } 277 : 278 2897 : char const* s0 = it; // start of value 279 : for(;;) 280 : { 281 : auto rv = grammar::parse( 282 2939 : it, end, grammar::tuple_rule( 283 2939 : grammar::token_rule( 284 2939 : ws_vchars), 285 2939 : crlf_rule)); 286 2939 : if(! rv) 287 488 : return rv.error(); 288 2451 : if(it == end) 289 : { 290 71 : BOOST_HTTP_PROTO_RETURN_EC( 291 : grammar::error::need_more); 292 : } 293 2380 : if( *it != ' ' && 294 2338 : *it != '\t') 295 : { 296 : // end of field 297 2338 : break; 298 : } 299 : // *it will match field_value_rule 300 42 : v.has_obs_fold = true; 301 42 : } 302 : 303 2338 : v.value = core::string_view(s0, (it - s0) - 2); 304 2338 : BOOST_ASSERT(! v.value.empty()); 305 : //BOOST_ASSERT(! ws(t.v.value.front())); 306 : 307 : // remove trailing SP,HTAB,CR,LF 308 2338 : auto p = &v.value.back(); 309 : for(;;) 310 : { 311 2543 : switch(*p) 312 : { 313 205 : case ' ': case '\t': 314 : case '\r': case '\n': 315 205 : --p; 316 205 : continue; 317 2338 : default: 318 2338 : ++p; 319 2338 : goto done; 320 : } 321 : } 322 2338 : done: 323 2338 : v.value = core::string_view( 324 : v.value.data(), 325 2338 : p - v.value.data()); 326 2338 : return v; 327 : } 328 : 329 : //------------------------------------------------ 330 : 331 : void 332 946 : remove_obs_fold( 333 : char* it, 334 : char const* const end) noexcept 335 : { 336 946 : while(it != end) 337 : { 338 941 : if(*it != '\r') 339 : { 340 593 : ++it; 341 593 : continue; 342 : } 343 348 : if(end - it < 3) 344 145 : break; 345 203 : BOOST_ASSERT(it[1] == '\n'); 346 406 : if( it[1] == '\n' && 347 203 : ws(it[2])) 348 : { 349 200 : it[0] = ' '; 350 200 : it[1] = ' '; 351 200 : it += 3; 352 : } 353 : else 354 : { 355 3 : ++it; 356 : } 357 : } 358 150 : } 359 : 360 : } // detail 361 : } // http_proto 362 : } // boost