| 1 | // |
| 2 | // Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot 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/boostorg/beast |
| 8 | // |
| 9 | |
| 10 | #ifndef BOOST_BEAST_HTTP_IMPL_FIELD_IPP |
| 11 | #define BOOST_BEAST_HTTP_IMPL_FIELD_IPP |
| 12 | |
| 13 | #include <boost/beast/http/field.hpp> |
| 14 | #include <boost/assert.hpp> |
| 15 | #include <algorithm> |
| 16 | #include <array> |
| 17 | #include <cstdint> |
| 18 | #include <cstring> |
| 19 | #include <ostream> |
| 20 | |
| 21 | |
| 22 | namespace boost { |
| 23 | namespace beast { |
| 24 | namespace http { |
| 25 | |
| 26 | namespace detail { |
| 27 | |
| 28 | struct field_table |
| 29 | { |
| 30 | static |
| 31 | std::uint32_t |
| 32 | get_chars( |
| 33 | unsigned char const* p) noexcept |
| 34 | { |
| 35 | // VFALCO memcpy is endian-dependent |
| 36 | //std::memcpy(&v, p, 4); |
| 37 | // Compiler should be smart enough to |
| 38 | // optimize this down to one instruction. |
| 39 | return |
| 40 | p[0] | |
| 41 | (p[1] << 8) | |
| 42 | (p[2] << 16) | |
| 43 | (p[3] << 24); |
| 44 | } |
| 45 | |
| 46 | using array_type = |
| 47 | std::array<string_view, 357>; |
| 48 | |
| 49 | // Strings are converted to lowercase |
| 50 | static |
| 51 | std::uint32_t |
| 52 | digest(string_view s) |
| 53 | { |
| 54 | std::uint32_t r = 0; |
| 55 | std::size_t n = s.size(); |
| 56 | auto p = reinterpret_cast< |
| 57 | unsigned char const*>(s.data()); |
| 58 | // consume N characters at a time |
| 59 | // VFALCO Can we do 8 on 64-bit systems? |
| 60 | while(n >= 4) |
| 61 | { |
| 62 | auto const v = get_chars(p); |
| 63 | r = (r * 5 + ( |
| 64 | v | 0x20202020 )); // convert to lower |
| 65 | p += 4; |
| 66 | n -= 4; |
| 67 | } |
| 68 | // handle remaining characters |
| 69 | while( n > 0 ) |
| 70 | { |
| 71 | r = r * 5 + ( *p | 0x20 ); |
| 72 | ++p; |
| 73 | --n; |
| 74 | } |
| 75 | return r; |
| 76 | } |
| 77 | |
| 78 | // This comparison is case-insensitive, and the |
| 79 | // strings must contain only valid http field characters. |
| 80 | static |
| 81 | bool |
| 82 | equals(string_view lhs, string_view rhs) |
| 83 | { |
| 84 | using Int = std::uint32_t; // VFALCO std::size_t? |
| 85 | auto n = lhs.size(); |
| 86 | if(n != rhs.size()) |
| 87 | return false; |
| 88 | auto p1 = reinterpret_cast< |
| 89 | unsigned char const*>(lhs.data()); |
| 90 | auto p2 = reinterpret_cast< |
| 91 | unsigned char const*>(rhs.data()); |
| 92 | auto constexpr S = sizeof(Int); |
| 93 | auto constexpr Mask = static_cast<Int>( |
| 94 | 0xDFDFDFDFDFDFDFDF & ~Int{0}); |
| 95 | for(; n >= S; p1 += S, p2 += S, n -= S) |
| 96 | { |
| 97 | Int const v1 = get_chars(p: p1); |
| 98 | Int const v2 = get_chars(p: p2); |
| 99 | if((v1 ^ v2) & Mask) |
| 100 | return false; |
| 101 | } |
| 102 | for(; n; ++p1, ++p2, --n) |
| 103 | if(( *p1 ^ *p2) & 0xDF) |
| 104 | return false; |
| 105 | return true; |
| 106 | } |
| 107 | |
| 108 | array_type by_name_; |
| 109 | |
| 110 | enum { N = 5155 }; |
| 111 | unsigned char map_[ N ][ 2 ] = {}; |
| 112 | |
| 113 | /* |
| 114 | From: |
| 115 | |
| 116 | https://www.iana.org/assignments/message-headers/message-headers.xhtml |
| 117 | */ |
| 118 | field_table() |
| 119 | : by_name_({._M_elems: { |
| 120 | // string constants |
| 121 | "<unknown-field>" , |
| 122 | "A-IM" , |
| 123 | "Accept" , |
| 124 | "Accept-Additions" , |
| 125 | "Accept-Charset" , |
| 126 | "Accept-Datetime" , |
| 127 | "Accept-Encoding" , |
| 128 | "Accept-Features" , |
| 129 | "Accept-Language" , |
| 130 | "Accept-Patch" , |
| 131 | "Accept-Post" , |
| 132 | "Accept-Ranges" , |
| 133 | "Access-Control" , |
| 134 | "Access-Control-Allow-Credentials" , |
| 135 | "Access-Control-Allow-Headers" , |
| 136 | "Access-Control-Allow-Methods" , |
| 137 | "Access-Control-Allow-Origin" , |
| 138 | "Access-Control-Expose-Headers" , |
| 139 | "Access-Control-Max-Age" , |
| 140 | "Access-Control-Request-Headers" , |
| 141 | "Access-Control-Request-Method" , |
| 142 | "Age" , |
| 143 | "Allow" , |
| 144 | "ALPN" , |
| 145 | "Also-Control" , |
| 146 | "Alt-Svc" , |
| 147 | "Alt-Used" , |
| 148 | "Alternate-Recipient" , |
| 149 | "Alternates" , |
| 150 | "Apparently-To" , |
| 151 | "Apply-To-Redirect-Ref" , |
| 152 | "Approved" , |
| 153 | "Archive" , |
| 154 | "Archived-At" , |
| 155 | "Article-Names" , |
| 156 | "Article-Updates" , |
| 157 | "Authentication-Control" , |
| 158 | "Authentication-Info" , |
| 159 | "Authentication-Results" , |
| 160 | "Authorization" , |
| 161 | "Auto-Submitted" , |
| 162 | "Autoforwarded" , |
| 163 | "Autosubmitted" , |
| 164 | "Base" , |
| 165 | "Bcc" , |
| 166 | "Body" , |
| 167 | "C-Ext" , |
| 168 | "C-Man" , |
| 169 | "C-Opt" , |
| 170 | "C-PEP" , |
| 171 | "C-PEP-Info" , |
| 172 | "Cache-Control" , |
| 173 | "CalDAV-Timezones" , |
| 174 | "Cancel-Key" , |
| 175 | "Cancel-Lock" , |
| 176 | "Cc" , |
| 177 | "Close" , |
| 178 | "Comments" , |
| 179 | "Compliance" , |
| 180 | "Connection" , |
| 181 | "Content-Alternative" , |
| 182 | "Content-Base" , |
| 183 | "Content-Description" , |
| 184 | "Content-Disposition" , |
| 185 | "Content-Duration" , |
| 186 | "Content-Encoding" , |
| 187 | "Content-features" , |
| 188 | "Content-ID" , |
| 189 | "Content-Identifier" , |
| 190 | "Content-Language" , |
| 191 | "Content-Length" , |
| 192 | "Content-Location" , |
| 193 | "Content-MD5" , |
| 194 | "Content-Range" , |
| 195 | "Content-Return" , |
| 196 | "Content-Script-Type" , |
| 197 | "Content-Style-Type" , |
| 198 | "Content-Transfer-Encoding" , |
| 199 | "Content-Type" , |
| 200 | "Content-Version" , |
| 201 | "Control" , |
| 202 | "Conversion" , |
| 203 | "Conversion-With-Loss" , |
| 204 | "Cookie" , |
| 205 | "Cookie2" , |
| 206 | "Cost" , |
| 207 | "DASL" , |
| 208 | "Date" , |
| 209 | "Date-Received" , |
| 210 | "DAV" , |
| 211 | "Default-Style" , |
| 212 | "Deferred-Delivery" , |
| 213 | "Delivery-Date" , |
| 214 | "Delta-Base" , |
| 215 | "Depth" , |
| 216 | "Derived-From" , |
| 217 | "Destination" , |
| 218 | "Differential-ID" , |
| 219 | "Digest" , |
| 220 | "Discarded-X400-IPMS-Extensions" , |
| 221 | "Discarded-X400-MTS-Extensions" , |
| 222 | "Disclose-Recipients" , |
| 223 | "Disposition-Notification-Options" , |
| 224 | "Disposition-Notification-To" , |
| 225 | "Distribution" , |
| 226 | "DKIM-Signature" , |
| 227 | "DL-Expansion-History" , |
| 228 | "Downgraded-Bcc" , |
| 229 | "Downgraded-Cc" , |
| 230 | "Downgraded-Disposition-Notification-To" , |
| 231 | "Downgraded-Final-Recipient" , |
| 232 | "Downgraded-From" , |
| 233 | "Downgraded-In-Reply-To" , |
| 234 | "Downgraded-Mail-From" , |
| 235 | "Downgraded-Message-Id" , |
| 236 | "Downgraded-Original-Recipient" , |
| 237 | "Downgraded-Rcpt-To" , |
| 238 | "Downgraded-References" , |
| 239 | "Downgraded-Reply-To" , |
| 240 | "Downgraded-Resent-Bcc" , |
| 241 | "Downgraded-Resent-Cc" , |
| 242 | "Downgraded-Resent-From" , |
| 243 | "Downgraded-Resent-Reply-To" , |
| 244 | "Downgraded-Resent-Sender" , |
| 245 | "Downgraded-Resent-To" , |
| 246 | "Downgraded-Return-Path" , |
| 247 | "Downgraded-Sender" , |
| 248 | "Downgraded-To" , |
| 249 | "EDIINT-Features" , |
| 250 | "Eesst-Version" , |
| 251 | "Encoding" , |
| 252 | "Encrypted" , |
| 253 | "Errors-To" , |
| 254 | "ETag" , |
| 255 | "Expect" , |
| 256 | "Expires" , |
| 257 | "Expiry-Date" , |
| 258 | "Ext" , |
| 259 | "Followup-To" , |
| 260 | "Forwarded" , |
| 261 | "From" , |
| 262 | "Generate-Delivery-Report" , |
| 263 | "GetProfile" , |
| 264 | "Hobareg" , |
| 265 | "Host" , |
| 266 | "HTTP2-Settings" , |
| 267 | "If" , |
| 268 | "If-Match" , |
| 269 | "If-Modified-Since" , |
| 270 | "If-None-Match" , |
| 271 | "If-Range" , |
| 272 | "If-Schedule-Tag-Match" , |
| 273 | "If-Unmodified-Since" , |
| 274 | "IM" , |
| 275 | "Importance" , |
| 276 | "In-Reply-To" , |
| 277 | "Incomplete-Copy" , |
| 278 | "Injection-Date" , |
| 279 | "Injection-Info" , |
| 280 | "Jabber-ID" , |
| 281 | "Keep-Alive" , |
| 282 | "Keywords" , |
| 283 | "Label" , |
| 284 | "Language" , |
| 285 | "Last-Modified" , |
| 286 | "Latest-Delivery-Time" , |
| 287 | "Lines" , |
| 288 | "Link" , |
| 289 | "List-Archive" , |
| 290 | "List-Help" , |
| 291 | "List-ID" , |
| 292 | "List-Owner" , |
| 293 | "List-Post" , |
| 294 | "List-Subscribe" , |
| 295 | "List-Unsubscribe" , |
| 296 | "List-Unsubscribe-Post" , |
| 297 | "Location" , |
| 298 | "Lock-Token" , |
| 299 | "Man" , |
| 300 | "Max-Forwards" , |
| 301 | "Memento-Datetime" , |
| 302 | "Message-Context" , |
| 303 | "Message-ID" , |
| 304 | "Message-Type" , |
| 305 | "Meter" , |
| 306 | "Method-Check" , |
| 307 | "Method-Check-Expires" , |
| 308 | "MIME-Version" , |
| 309 | "MMHS-Acp127-Message-Identifier" , |
| 310 | "MMHS-Authorizing-Users" , |
| 311 | "MMHS-Codress-Message-Indicator" , |
| 312 | "MMHS-Copy-Precedence" , |
| 313 | "MMHS-Exempted-Address" , |
| 314 | "MMHS-Extended-Authorisation-Info" , |
| 315 | "MMHS-Handling-Instructions" , |
| 316 | "MMHS-Message-Instructions" , |
| 317 | "MMHS-Message-Type" , |
| 318 | "MMHS-Originator-PLAD" , |
| 319 | "MMHS-Originator-Reference" , |
| 320 | "MMHS-Other-Recipients-Indicator-CC" , |
| 321 | "MMHS-Other-Recipients-Indicator-To" , |
| 322 | "MMHS-Primary-Precedence" , |
| 323 | "MMHS-Subject-Indicator-Codes" , |
| 324 | "MT-Priority" , |
| 325 | "Negotiate" , |
| 326 | "Newsgroups" , |
| 327 | "NNTP-Posting-Date" , |
| 328 | "NNTP-Posting-Host" , |
| 329 | "Non-Compliance" , |
| 330 | "Obsoletes" , |
| 331 | "Opt" , |
| 332 | "Optional" , |
| 333 | "Optional-WWW-Authenticate" , |
| 334 | "Ordering-Type" , |
| 335 | "Organization" , |
| 336 | "Origin" , |
| 337 | "Original-Encoded-Information-Types" , |
| 338 | "Original-From" , |
| 339 | "Original-Message-ID" , |
| 340 | "Original-Recipient" , |
| 341 | "Original-Sender" , |
| 342 | "Original-Subject" , |
| 343 | "Originator-Return-Address" , |
| 344 | "Overwrite" , |
| 345 | "P3P" , |
| 346 | "Path" , |
| 347 | "PEP" , |
| 348 | "Pep-Info" , |
| 349 | "PICS-Label" , |
| 350 | "Position" , |
| 351 | "Posting-Version" , |
| 352 | "Pragma" , |
| 353 | "Prefer" , |
| 354 | "Preference-Applied" , |
| 355 | "Prevent-NonDelivery-Report" , |
| 356 | "Priority" , |
| 357 | "Privicon" , |
| 358 | "ProfileObject" , |
| 359 | "Protocol" , |
| 360 | "Protocol-Info" , |
| 361 | "Protocol-Query" , |
| 362 | "Protocol-Request" , |
| 363 | "Proxy-Authenticate" , |
| 364 | "Proxy-Authentication-Info" , |
| 365 | "Proxy-Authorization" , |
| 366 | "Proxy-Connection" , |
| 367 | "Proxy-Features" , |
| 368 | "Proxy-Instruction" , |
| 369 | "Public" , |
| 370 | "Public-Key-Pins" , |
| 371 | "Public-Key-Pins-Report-Only" , |
| 372 | "Range" , |
| 373 | "Received" , |
| 374 | "Received-SPF" , |
| 375 | "Redirect-Ref" , |
| 376 | "References" , |
| 377 | "Referer" , |
| 378 | "Referer-Root" , |
| 379 | "Relay-Version" , |
| 380 | "Reply-By" , |
| 381 | "Reply-To" , |
| 382 | "Require-Recipient-Valid-Since" , |
| 383 | "Resent-Bcc" , |
| 384 | "Resent-Cc" , |
| 385 | "Resent-Date" , |
| 386 | "Resent-From" , |
| 387 | "Resent-Message-ID" , |
| 388 | "Resent-Reply-To" , |
| 389 | "Resent-Sender" , |
| 390 | "Resent-To" , |
| 391 | "Resolution-Hint" , |
| 392 | "Resolver-Location" , |
| 393 | "Retry-After" , |
| 394 | "Return-Path" , |
| 395 | "Safe" , |
| 396 | "Schedule-Reply" , |
| 397 | "Schedule-Tag" , |
| 398 | "Sec-Fetch-Dest" , |
| 399 | "Sec-Fetch-Mode" , |
| 400 | "Sec-Fetch-Site" , |
| 401 | "Sec-Fetch-User" , |
| 402 | "Sec-WebSocket-Accept" , |
| 403 | "Sec-WebSocket-Extensions" , |
| 404 | "Sec-WebSocket-Key" , |
| 405 | "Sec-WebSocket-Protocol" , |
| 406 | "Sec-WebSocket-Version" , |
| 407 | "Security-Scheme" , |
| 408 | "See-Also" , |
| 409 | "Sender" , |
| 410 | "Sensitivity" , |
| 411 | "Server" , |
| 412 | "Set-Cookie" , |
| 413 | "Set-Cookie2" , |
| 414 | "SetProfile" , |
| 415 | "SIO-Label" , |
| 416 | "SIO-Label-History" , |
| 417 | "SLUG" , |
| 418 | "SoapAction" , |
| 419 | "Solicitation" , |
| 420 | "Status-URI" , |
| 421 | "Strict-Transport-Security" , |
| 422 | "Subject" , |
| 423 | "SubOK" , |
| 424 | "Subst" , |
| 425 | "Summary" , |
| 426 | "Supersedes" , |
| 427 | "Surrogate-Capability" , |
| 428 | "Surrogate-Control" , |
| 429 | "TCN" , |
| 430 | "TE" , |
| 431 | "Timeout" , |
| 432 | "Title" , |
| 433 | "To" , |
| 434 | "Topic" , |
| 435 | "Trailer" , |
| 436 | "Transfer-Encoding" , |
| 437 | "TTL" , |
| 438 | "UA-Color" , |
| 439 | "UA-Media" , |
| 440 | "UA-Pixels" , |
| 441 | "UA-Resolution" , |
| 442 | "UA-Windowpixels" , |
| 443 | "Upgrade" , |
| 444 | "Urgency" , |
| 445 | "URI" , |
| 446 | "User-Agent" , |
| 447 | "Variant-Vary" , |
| 448 | "Vary" , |
| 449 | "VBR-Info" , |
| 450 | "Version" , |
| 451 | "Via" , |
| 452 | "Want-Digest" , |
| 453 | "Warning" , |
| 454 | "WWW-Authenticate" , |
| 455 | "X-Archived-At" , |
| 456 | "X-Device-Accept" , |
| 457 | "X-Device-Accept-Charset" , |
| 458 | "X-Device-Accept-Encoding" , |
| 459 | "X-Device-Accept-Language" , |
| 460 | "X-Device-User-Agent" , |
| 461 | "X-Frame-Options" , |
| 462 | "X-Mittente" , |
| 463 | "X-PGP-Sig" , |
| 464 | "X-Ricevuta" , |
| 465 | "X-Riferimento-Message-ID" , |
| 466 | "X-TipoRicevuta" , |
| 467 | "X-Trasporto" , |
| 468 | "X-VerificaSicurezza" , |
| 469 | "X400-Content-Identifier" , |
| 470 | "X400-Content-Return" , |
| 471 | "X400-Content-Type" , |
| 472 | "X400-MTS-Identifier" , |
| 473 | "X400-Originator" , |
| 474 | "X400-Received" , |
| 475 | "X400-Recipients" , |
| 476 | "X400-Trace" , |
| 477 | "Xref" |
| 478 | }}) |
| 479 | { |
| 480 | for(std::size_t i = 1, n = 256; i < n; ++i) |
| 481 | { |
| 482 | auto sv = by_name_[ i ]; |
| 483 | auto h = digest(s: sv); |
| 484 | auto j = h % N; |
| 485 | BOOST_ASSERT(map_[j][0] == 0); |
| 486 | map_[j][0] = static_cast<unsigned char>(i); |
| 487 | } |
| 488 | |
| 489 | for(std::size_t i = 256, n = by_name_.size(); i < n; ++i) |
| 490 | { |
| 491 | auto sv = by_name_[i]; |
| 492 | auto h = digest(s: sv); |
| 493 | auto j = h % N; |
| 494 | BOOST_ASSERT(map_[j][1] == 0); |
| 495 | map_[j][1] = static_cast<unsigned char>(i - 255); |
| 496 | } |
| 497 | } |
| 498 | |
| 499 | field |
| 500 | string_to_field(string_view s) const |
| 501 | { |
| 502 | auto h = digest(s); |
| 503 | auto j = h % N; |
| 504 | int i = map_[j][0]; |
| 505 | string_view s2 = by_name_[i]; |
| 506 | if(i != 0 && equals(lhs: s, rhs: s2)) |
| 507 | return static_cast<field>(i); |
| 508 | i = map_[j][1]; |
| 509 | if(i == 0) |
| 510 | return field::unknown; |
| 511 | i += 255; |
| 512 | s2 = by_name_[i]; |
| 513 | |
| 514 | if(equals(lhs: s, rhs: s2)) |
| 515 | return static_cast<field>(i); |
| 516 | return field::unknown; |
| 517 | } |
| 518 | |
| 519 | // |
| 520 | // Deprecated |
| 521 | // |
| 522 | |
| 523 | using const_iterator = |
| 524 | array_type::const_iterator; |
| 525 | |
| 526 | std::size_t |
| 527 | size() const |
| 528 | { |
| 529 | return by_name_.size(); |
| 530 | } |
| 531 | |
| 532 | const_iterator |
| 533 | begin() const |
| 534 | { |
| 535 | return by_name_.begin(); |
| 536 | } |
| 537 | |
| 538 | const_iterator |
| 539 | end() const |
| 540 | { |
| 541 | return by_name_.end(); |
| 542 | } |
| 543 | }; |
| 544 | |
| 545 | BOOST_BEAST_DECL |
| 546 | field_table const& |
| 547 | get_field_table() |
| 548 | { |
| 549 | static field_table const tab; |
| 550 | return tab; |
| 551 | } |
| 552 | |
| 553 | BOOST_BEAST_DECL |
| 554 | string_view |
| 555 | to_string(field f) |
| 556 | { |
| 557 | auto const& v = get_field_table(); |
| 558 | BOOST_ASSERT(static_cast<unsigned>(f) < v.size()); |
| 559 | return v.begin()[static_cast<unsigned>(f)]; |
| 560 | } |
| 561 | |
| 562 | } // detail |
| 563 | |
| 564 | string_view |
| 565 | to_string(field f) |
| 566 | { |
| 567 | return detail::to_string(f); |
| 568 | } |
| 569 | |
| 570 | field |
| 571 | string_to_field(string_view s) |
| 572 | { |
| 573 | return detail::get_field_table().string_to_field(s); |
| 574 | } |
| 575 | |
| 576 | std::ostream& |
| 577 | operator<<(std::ostream& os, field f) |
| 578 | { |
| 579 | return os << to_string(f); |
| 580 | } |
| 581 | |
| 582 | } // http |
| 583 | } // beast |
| 584 | } // boost |
| 585 | |
| 586 | #endif |
| 587 | |