| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 // The rules for parsing content-types were borrowed from Firefox: | 5 // The rules for parsing content-types were borrowed from Firefox: |
| 6 // http://lxr.mozilla.org/mozilla/source/netwerk/base/src/nsURLHelper.cpp#834 | 6 // http://lxr.mozilla.org/mozilla/source/netwerk/base/src/nsURLHelper.cpp#834 |
| 7 | 7 |
| 8 #include "net/http/http_util.h" | 8 #include "net/http/http_util.h" |
| 9 | 9 |
| 10 #include <algorithm> | 10 #include <algorithm> |
| (...skipping 427 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 438 while (*begin < *end && IsLWS((*end)[-1])) | 438 while (*begin < *end && IsLWS((*end)[-1])) |
| 439 --(*end); | 439 --(*end); |
| 440 } | 440 } |
| 441 | 441 |
| 442 bool HttpUtil::IsQuote(char c) { | 442 bool HttpUtil::IsQuote(char c) { |
| 443 // Single quote mark isn't actually part of quoted-text production, | 443 // Single quote mark isn't actually part of quoted-text production, |
| 444 // but apparently some servers rely on this. | 444 // but apparently some servers rely on this. |
| 445 return c == '"' || c == '\''; | 445 return c == '"' || c == '\''; |
| 446 } | 446 } |
| 447 | 447 |
| 448 namespace { |
| 449 bool IsTokenChar(unsigned char c) { |
| 450 return !(c >= 0x80 || c <= 0x1F || c == 0x7F || c == '(' || c == ')' || |
| 451 c == '<' || c == '>' || c == '@' || c == ',' || c == ';' || |
| 452 c == ':' || c == '\\' || c == '"' || c == '/' || c == '[' || |
| 453 c == ']' || c == '?' || c == '=' || c == '{' || c == '}' || |
| 454 c == ' ' || c == '\t'); |
| 455 } |
| 456 } // anonymous namespace |
| 457 |
| 448 // See RFC 2616 Sec 2.2 for the definition of |token|. | 458 // See RFC 2616 Sec 2.2 for the definition of |token|. |
| 449 bool HttpUtil::IsToken(std::string::const_iterator begin, | 459 bool HttpUtil::IsToken(std::string::const_iterator begin, |
| 450 std::string::const_iterator end) { | 460 std::string::const_iterator end) { |
| 451 if (begin == end) | 461 if (begin == end) |
| 452 return false; | 462 return false; |
| 453 for (std::string::const_iterator iter = begin; iter != end; ++iter) { | 463 for (std::string::const_iterator iter = begin; iter != end; ++iter) { |
| 454 unsigned char c = *iter; | 464 if (!IsTokenChar(*iter)) |
| 455 if (c >= 0x80 || c <= 0x1F || c == 0x7F || | |
| 456 c == '(' || c == ')' || c == '<' || c == '>' || c == '@' || | |
| 457 c == ',' || c == ';' || c == ':' || c == '\\' || c == '"' || | |
| 458 c == '/' || c == '[' || c == ']' || c == '?' || c == '=' || | |
| 459 c == '{' || c == '}' || c == ' ' || c == '\t') | |
| 460 return false; | 465 return false; |
| 461 } | 466 } |
| 462 return true; | 467 return true; |
| 463 } | 468 } |
| 464 | 469 |
| 465 std::string HttpUtil::Unquote(std::string::const_iterator begin, | 470 // See RFC 5987 Sec 3.2.1 for the definition of |parmname|. |
| 466 std::string::const_iterator end) { | 471 bool HttpUtil::IsParmName(std::string::const_iterator begin, |
| 472 std::string::const_iterator end) { |
| 473 if (begin == end) |
| 474 return false; |
| 475 for (std::string::const_iterator iter = begin; iter != end; ++iter) { |
| 476 unsigned char c = *iter; |
| 477 if (!IsTokenChar(c) || c == '*' || c == '\'' || c == '%') |
| 478 return false; |
| 479 } |
| 480 return true; |
| 481 } |
| 482 |
| 483 namespace { |
| 484 bool UnquoteImpl(std::string::const_iterator begin, |
| 485 std::string::const_iterator end, |
| 486 bool strict_quotes, |
| 487 std::string* out) { |
| 467 // Empty string | 488 // Empty string |
| 468 if (begin == end) | 489 if (begin == end) |
| 469 return std::string(); | 490 return false; |
| 470 | 491 |
| 471 // Nothing to unquote. | 492 // Nothing to unquote. |
| 472 if (!IsQuote(*begin)) | 493 if (!HttpUtil::IsQuote(*begin)) |
| 473 return std::string(begin, end); | 494 return false; |
| 474 | 495 |
| 475 // No terminal quote mark. | 496 // No terminal quote mark. |
| 476 if (end - begin < 2 || *begin != *(end - 1)) | 497 if (end - begin < 2 || *begin != *(end - 1)) |
| 477 return std::string(begin, end); | 498 return false; |
| 499 |
| 500 char quote = *begin; |
| 478 | 501 |
| 479 // Strip quotemarks | 502 // Strip quotemarks |
| 480 ++begin; | 503 ++begin; |
| 481 --end; | 504 --end; |
| 482 | 505 |
| 483 // Unescape quoted-pair (defined in RFC 2616 section 2.2) | 506 // Unescape quoted-pair (defined in RFC 2616 section 2.2) |
| 507 bool prev_escape = false; |
| 484 std::string unescaped; | 508 std::string unescaped; |
| 485 bool prev_escape = false; | |
| 486 for (; begin != end; ++begin) { | 509 for (; begin != end; ++begin) { |
| 487 char c = *begin; | 510 char c = *begin; |
| 488 if (c == '\\' && !prev_escape) { | 511 if (c == '\\' && !prev_escape) { |
| 489 prev_escape = true; | 512 prev_escape = true; |
| 490 continue; | 513 continue; |
| 491 } | 514 } |
| 515 if (strict_quotes && !prev_escape && c == quote) |
| 516 return false; |
| 492 prev_escape = false; | 517 prev_escape = false; |
| 493 unescaped.push_back(c); | 518 unescaped.push_back(c); |
| 494 } | 519 } |
| 495 return unescaped; | 520 |
| 521 // Terminal quote is escaped. |
| 522 if (strict_quotes && prev_escape) |
| 523 return false; |
| 524 |
| 525 *out = std::move(unescaped); |
| 526 return true; |
| 527 } |
| 528 } // anonymous namespace |
| 529 |
| 530 std::string HttpUtil::Unquote(std::string::const_iterator begin, |
| 531 std::string::const_iterator end) { |
| 532 std::string result; |
| 533 if (!UnquoteImpl(begin, end, false, &result)) |
| 534 return std::string(begin, end); |
| 535 |
| 536 return result; |
| 496 } | 537 } |
| 497 | 538 |
| 498 // static | 539 // static |
| 499 std::string HttpUtil::Unquote(const std::string& str) { | 540 std::string HttpUtil::Unquote(const std::string& str) { |
| 500 return Unquote(str.begin(), str.end()); | 541 return Unquote(str.begin(), str.end()); |
| 501 } | 542 } |
| 502 | 543 |
| 503 // static | 544 // static |
| 545 bool HttpUtil::StrictUnquote(std::string::const_iterator begin, |
| 546 std::string::const_iterator end, |
| 547 std::string* out) { |
| 548 return UnquoteImpl(begin, end, true, out); |
| 549 } |
| 550 |
| 551 // static |
| 552 bool HttpUtil::StrictUnquote(const std::string& str, std::string* out) { |
| 553 return StrictUnquote(str.begin(), str.end(), out); |
| 554 } |
| 555 |
| 556 // static |
| 504 std::string HttpUtil::Quote(const std::string& str) { | 557 std::string HttpUtil::Quote(const std::string& str) { |
| 505 std::string escaped; | 558 std::string escaped; |
| 506 escaped.reserve(2 + str.size()); | 559 escaped.reserve(2 + str.size()); |
| 507 | 560 |
| 508 std::string::const_iterator begin = str.begin(); | 561 std::string::const_iterator begin = str.begin(); |
| 509 std::string::const_iterator end = str.end(); | 562 std::string::const_iterator end = str.end(); |
| 510 | 563 |
| 511 // Esape any backslashes or quotemarks within the string, and | 564 // Esape any backslashes or quotemarks within the string, and |
| 512 // then surround with quotes. | 565 // then surround with quotes. |
| 513 escaped.push_back('"'); | 566 escaped.push_back('"'); |
| (...skipping 389 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 903 if (value_begin_ != value_end_) | 956 if (value_begin_ != value_end_) |
| 904 return true; | 957 return true; |
| 905 } | 958 } |
| 906 return false; | 959 return false; |
| 907 } | 960 } |
| 908 | 961 |
| 909 HttpUtil::NameValuePairsIterator::NameValuePairsIterator( | 962 HttpUtil::NameValuePairsIterator::NameValuePairsIterator( |
| 910 std::string::const_iterator begin, | 963 std::string::const_iterator begin, |
| 911 std::string::const_iterator end, | 964 std::string::const_iterator end, |
| 912 char delimiter, | 965 char delimiter, |
| 913 OptionalValues optional_values) | 966 Values optional_values, |
| 967 Quotes strict_quotes) |
| 914 : props_(begin, end, delimiter), | 968 : props_(begin, end, delimiter), |
| 915 valid_(true), | 969 valid_(true), |
| 916 name_begin_(end), | 970 name_begin_(end), |
| 917 name_end_(end), | 971 name_end_(end), |
| 918 value_begin_(end), | 972 value_begin_(end), |
| 919 value_end_(end), | 973 value_end_(end), |
| 920 value_is_quoted_(false), | 974 value_is_quoted_(false), |
| 921 values_optional_(optional_values == VALUES_OPTIONAL) {} | 975 values_optional_(optional_values == Values::OPTIONAL), |
| 976 strict_quotes_(strict_quotes == Quotes::STRICT) {} |
| 922 | 977 |
| 923 HttpUtil::NameValuePairsIterator::NameValuePairsIterator( | 978 HttpUtil::NameValuePairsIterator::NameValuePairsIterator( |
| 924 std::string::const_iterator begin, | 979 std::string::const_iterator begin, |
| 925 std::string::const_iterator end, | 980 std::string::const_iterator end, |
| 926 char delimiter) | 981 char delimiter) |
| 927 : NameValuePairsIterator(begin, end, delimiter, VALUES_NOT_OPTIONAL) {} | 982 : NameValuePairsIterator(begin, |
| 983 end, |
| 984 delimiter, |
| 985 Values::NOT_OPTIONAL, |
| 986 Quotes::NOT_STRICT) {} |
| 928 | 987 |
| 929 HttpUtil::NameValuePairsIterator::NameValuePairsIterator( | 988 HttpUtil::NameValuePairsIterator::NameValuePairsIterator( |
| 930 const NameValuePairsIterator& other) = default; | 989 const NameValuePairsIterator& other) = default; |
| 931 | 990 |
| 932 HttpUtil::NameValuePairsIterator::~NameValuePairsIterator() {} | 991 HttpUtil::NameValuePairsIterator::~NameValuePairsIterator() {} |
| 933 | 992 |
| 934 // We expect properties to be formatted as one of: | 993 // We expect properties to be formatted as one of: |
| 935 // name="value" | 994 // name="value" |
| 936 // name='value' | 995 // name='value' |
| 937 // name='\'value\'' | 996 // name='\'value\'' |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 973 TrimLWS(&value_begin_, &value_end_); | 1032 TrimLWS(&value_begin_, &value_end_); |
| 974 value_is_quoted_ = false; | 1033 value_is_quoted_ = false; |
| 975 unquoted_value_.clear(); | 1034 unquoted_value_.clear(); |
| 976 | 1035 |
| 977 if (equals != value_end_ && value_begin_ == value_end_) { | 1036 if (equals != value_end_ && value_begin_ == value_end_) { |
| 978 // Malformed; value is empty | 1037 // Malformed; value is empty |
| 979 return valid_ = false; | 1038 return valid_ = false; |
| 980 } | 1039 } |
| 981 | 1040 |
| 982 if (value_begin_ != value_end_ && HttpUtil::IsQuote(*value_begin_)) { | 1041 if (value_begin_ != value_end_ && HttpUtil::IsQuote(*value_begin_)) { |
| 1042 value_is_quoted_ = true; |
| 1043 |
| 1044 if (strict_quotes_) { |
| 1045 if (!HttpUtil::StrictUnquote(value_begin_, value_end_, &unquoted_value_)) |
| 1046 return valid_ = false; |
| 1047 return true; |
| 1048 } |
| 1049 |
| 983 // Trim surrounding quotemarks off the value | 1050 // Trim surrounding quotemarks off the value |
| 984 if (*value_begin_ != *(value_end_ - 1) || value_begin_ + 1 == value_end_) { | 1051 if (*value_begin_ != *(value_end_ - 1) || value_begin_ + 1 == value_end_) { |
| 985 // NOTE: This is not as graceful as it sounds: | 1052 // NOTE: This is not as graceful as it sounds: |
| 986 // * quoted-pairs will no longer be unquoted | 1053 // * quoted-pairs will no longer be unquoted |
| 987 // (["\"hello] should give ["hello]). | 1054 // (["\"hello] should give ["hello]). |
| 988 // * Does not detect when the final quote is escaped | 1055 // * Does not detect when the final quote is escaped |
| 989 // (["value\"] should give [value"]) | 1056 // (["value\"] should give [value"]) |
| 1057 value_is_quoted_ = false; |
| 990 ++value_begin_; // Gracefully recover from mismatching quotes. | 1058 ++value_begin_; // Gracefully recover from mismatching quotes. |
| 991 } else { | 1059 } else { |
| 992 value_is_quoted_ = true; | |
| 993 // Do not store iterators into this. See declaration of unquoted_value_. | 1060 // Do not store iterators into this. See declaration of unquoted_value_. |
| 994 unquoted_value_ = HttpUtil::Unquote(value_begin_, value_end_); | 1061 unquoted_value_ = HttpUtil::Unquote(value_begin_, value_end_); |
| 995 } | 1062 } |
| 996 } | 1063 } |
| 997 | 1064 |
| 998 return true; | 1065 return true; |
| 999 } | 1066 } |
| 1000 | 1067 |
| 1001 } // namespace net | 1068 } // namespace net |
| OLD | NEW |