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; |
| 495 |
| 496 // Anything other than double quotes in strict mode. |
| 497 if (strict_quotes && *begin != '"') |
| 498 return false; |
474 | 499 |
475 // No terminal quote mark. | 500 // No terminal quote mark. |
476 if (end - begin < 2 || *begin != *(end - 1)) | 501 if (end - begin < 2 || *begin != *(end - 1)) |
477 return std::string(begin, end); | 502 return false; |
| 503 |
| 504 char quote = *begin; |
478 | 505 |
479 // Strip quotemarks | 506 // Strip quotemarks |
480 ++begin; | 507 ++begin; |
481 --end; | 508 --end; |
482 | 509 |
483 // Unescape quoted-pair (defined in RFC 2616 section 2.2) | 510 // Unescape quoted-pair (defined in RFC 2616 section 2.2) |
| 511 bool prev_escape = false; |
484 std::string unescaped; | 512 std::string unescaped; |
485 bool prev_escape = false; | |
486 for (; begin != end; ++begin) { | 513 for (; begin != end; ++begin) { |
487 char c = *begin; | 514 char c = *begin; |
488 if (c == '\\' && !prev_escape) { | 515 if (c == '\\' && !prev_escape) { |
489 prev_escape = true; | 516 prev_escape = true; |
490 continue; | 517 continue; |
491 } | 518 } |
| 519 if (strict_quotes && !prev_escape && c == quote) |
| 520 return false; |
492 prev_escape = false; | 521 prev_escape = false; |
493 unescaped.push_back(c); | 522 unescaped.push_back(c); |
494 } | 523 } |
495 return unescaped; | 524 |
| 525 // Terminal quote is escaped. |
| 526 if (strict_quotes && prev_escape) |
| 527 return false; |
| 528 |
| 529 *out = std::move(unescaped); |
| 530 return true; |
| 531 } |
| 532 } // anonymous namespace |
| 533 |
| 534 std::string HttpUtil::Unquote(std::string::const_iterator begin, |
| 535 std::string::const_iterator end) { |
| 536 std::string result; |
| 537 if (!UnquoteImpl(begin, end, false, &result)) |
| 538 return std::string(begin, end); |
| 539 |
| 540 return result; |
496 } | 541 } |
497 | 542 |
498 // static | 543 // static |
499 std::string HttpUtil::Unquote(const std::string& str) { | 544 std::string HttpUtil::Unquote(const std::string& str) { |
500 return Unquote(str.begin(), str.end()); | 545 return Unquote(str.begin(), str.end()); |
501 } | 546 } |
502 | 547 |
503 // static | 548 // static |
| 549 bool HttpUtil::StrictUnquote(std::string::const_iterator begin, |
| 550 std::string::const_iterator end, |
| 551 std::string* out) { |
| 552 return UnquoteImpl(begin, end, true, out); |
| 553 } |
| 554 |
| 555 // static |
| 556 bool HttpUtil::StrictUnquote(const std::string& str, std::string* out) { |
| 557 return StrictUnquote(str.begin(), str.end(), out); |
| 558 } |
| 559 |
| 560 // static |
504 std::string HttpUtil::Quote(const std::string& str) { | 561 std::string HttpUtil::Quote(const std::string& str) { |
505 std::string escaped; | 562 std::string escaped; |
506 escaped.reserve(2 + str.size()); | 563 escaped.reserve(2 + str.size()); |
507 | 564 |
508 std::string::const_iterator begin = str.begin(); | 565 std::string::const_iterator begin = str.begin(); |
509 std::string::const_iterator end = str.end(); | 566 std::string::const_iterator end = str.end(); |
510 | 567 |
511 // Esape any backslashes or quotemarks within the string, and | 568 // Esape any backslashes or quotemarks within the string, and |
512 // then surround with quotes. | 569 // then surround with quotes. |
513 escaped.push_back('"'); | 570 escaped.push_back('"'); |
(...skipping 389 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
903 if (value_begin_ != value_end_) | 960 if (value_begin_ != value_end_) |
904 return true; | 961 return true; |
905 } | 962 } |
906 return false; | 963 return false; |
907 } | 964 } |
908 | 965 |
909 HttpUtil::NameValuePairsIterator::NameValuePairsIterator( | 966 HttpUtil::NameValuePairsIterator::NameValuePairsIterator( |
910 std::string::const_iterator begin, | 967 std::string::const_iterator begin, |
911 std::string::const_iterator end, | 968 std::string::const_iterator end, |
912 char delimiter, | 969 char delimiter, |
913 OptionalValues optional_values) | 970 Values optional_values, |
| 971 Quotes strict_quotes) |
914 : props_(begin, end, delimiter), | 972 : props_(begin, end, delimiter), |
915 valid_(true), | 973 valid_(true), |
916 name_begin_(end), | 974 name_begin_(end), |
917 name_end_(end), | 975 name_end_(end), |
918 value_begin_(end), | 976 value_begin_(end), |
919 value_end_(end), | 977 value_end_(end), |
920 value_is_quoted_(false), | 978 value_is_quoted_(false), |
921 values_optional_(optional_values == VALUES_OPTIONAL) {} | 979 values_optional_(optional_values == Values::NOT_REQUIRED), |
| 980 strict_quotes_(strict_quotes == Quotes::STRICT_QUOTES) { |
| 981 if (strict_quotes_) |
| 982 props_.set_quote_chars("\""); |
| 983 } |
922 | 984 |
923 HttpUtil::NameValuePairsIterator::NameValuePairsIterator( | 985 HttpUtil::NameValuePairsIterator::NameValuePairsIterator( |
924 std::string::const_iterator begin, | 986 std::string::const_iterator begin, |
925 std::string::const_iterator end, | 987 std::string::const_iterator end, |
926 char delimiter) | 988 char delimiter) |
927 : NameValuePairsIterator(begin, end, delimiter, VALUES_NOT_OPTIONAL) {} | 989 : NameValuePairsIterator(begin, |
| 990 end, |
| 991 delimiter, |
| 992 Values::REQUIRED, |
| 993 Quotes::NOT_STRICT) {} |
928 | 994 |
929 HttpUtil::NameValuePairsIterator::NameValuePairsIterator( | 995 HttpUtil::NameValuePairsIterator::NameValuePairsIterator( |
930 const NameValuePairsIterator& other) = default; | 996 const NameValuePairsIterator& other) = default; |
931 | 997 |
932 HttpUtil::NameValuePairsIterator::~NameValuePairsIterator() {} | 998 HttpUtil::NameValuePairsIterator::~NameValuePairsIterator() {} |
933 | 999 |
934 // We expect properties to be formatted as one of: | 1000 // We expect properties to be formatted as one of: |
935 // name="value" | 1001 // name="value" |
936 // name='value' | 1002 // name='value' |
937 // name='\'value\'' | 1003 // name='\'value\'' |
(...skipping 15 matching lines...) Expand all Loading... |
953 // Scan for the equals sign. | 1019 // Scan for the equals sign. |
954 std::string::const_iterator equals = std::find(value_begin_, value_end_, '='); | 1020 std::string::const_iterator equals = std::find(value_begin_, value_end_, '='); |
955 if (equals == value_begin_) | 1021 if (equals == value_begin_) |
956 return valid_ = false; // Malformed, no name | 1022 return valid_ = false; // Malformed, no name |
957 if (equals == value_end_ && !values_optional_) | 1023 if (equals == value_end_ && !values_optional_) |
958 return valid_ = false; // Malformed, no equals sign and values are required | 1024 return valid_ = false; // Malformed, no equals sign and values are required |
959 | 1025 |
960 // If an equals sign was found, verify that it wasn't inside of quote marks. | 1026 // If an equals sign was found, verify that it wasn't inside of quote marks. |
961 if (equals != value_end_) { | 1027 if (equals != value_end_) { |
962 for (std::string::const_iterator it = value_begin_; it != equals; ++it) { | 1028 for (std::string::const_iterator it = value_begin_; it != equals; ++it) { |
963 if (HttpUtil::IsQuote(*it)) | 1029 if (IsQuote(*it)) |
964 return valid_ = false; // Malformed, quote appears before equals sign | 1030 return valid_ = false; // Malformed, quote appears before equals sign |
965 } | 1031 } |
966 } | 1032 } |
967 | 1033 |
968 name_begin_ = value_begin_; | 1034 name_begin_ = value_begin_; |
969 name_end_ = equals; | 1035 name_end_ = equals; |
970 value_begin_ = (equals == value_end_) ? value_end_ : equals + 1; | 1036 value_begin_ = (equals == value_end_) ? value_end_ : equals + 1; |
971 | 1037 |
972 TrimLWS(&name_begin_, &name_end_); | 1038 TrimLWS(&name_begin_, &name_end_); |
973 TrimLWS(&value_begin_, &value_end_); | 1039 TrimLWS(&value_begin_, &value_end_); |
974 value_is_quoted_ = false; | 1040 value_is_quoted_ = false; |
975 unquoted_value_.clear(); | 1041 unquoted_value_.clear(); |
976 | 1042 |
977 if (equals != value_end_ && value_begin_ == value_end_) { | 1043 if (equals != value_end_ && value_begin_ == value_end_) { |
978 // Malformed; value is empty | 1044 // Malformed; value is empty |
979 return valid_ = false; | 1045 return valid_ = false; |
980 } | 1046 } |
981 | 1047 |
982 if (value_begin_ != value_end_ && HttpUtil::IsQuote(*value_begin_)) { | 1048 if (value_begin_ != value_end_ && IsQuote(*value_begin_)) { |
| 1049 value_is_quoted_ = true; |
| 1050 |
| 1051 if (strict_quotes_) { |
| 1052 if (!HttpUtil::StrictUnquote(value_begin_, value_end_, &unquoted_value_)) |
| 1053 return valid_ = false; |
| 1054 return true; |
| 1055 } |
| 1056 |
983 // Trim surrounding quotemarks off the value | 1057 // Trim surrounding quotemarks off the value |
984 if (*value_begin_ != *(value_end_ - 1) || value_begin_ + 1 == value_end_) { | 1058 if (*value_begin_ != *(value_end_ - 1) || value_begin_ + 1 == value_end_) { |
985 // NOTE: This is not as graceful as it sounds: | 1059 // NOTE: This is not as graceful as it sounds: |
986 // * quoted-pairs will no longer be unquoted | 1060 // * quoted-pairs will no longer be unquoted |
987 // (["\"hello] should give ["hello]). | 1061 // (["\"hello] should give ["hello]). |
988 // * Does not detect when the final quote is escaped | 1062 // * Does not detect when the final quote is escaped |
989 // (["value\"] should give [value"]) | 1063 // (["value\"] should give [value"]) |
| 1064 value_is_quoted_ = false; |
990 ++value_begin_; // Gracefully recover from mismatching quotes. | 1065 ++value_begin_; // Gracefully recover from mismatching quotes. |
991 } else { | 1066 } else { |
992 value_is_quoted_ = true; | |
993 // Do not store iterators into this. See declaration of unquoted_value_. | 1067 // Do not store iterators into this. See declaration of unquoted_value_. |
994 unquoted_value_ = HttpUtil::Unquote(value_begin_, value_end_); | 1068 unquoted_value_ = HttpUtil::Unquote(value_begin_, value_end_); |
995 } | 1069 } |
996 } | 1070 } |
997 | 1071 |
998 return true; | 1072 return true; |
999 } | 1073 } |
1000 | 1074 |
| 1075 bool HttpUtil::NameValuePairsIterator::IsQuote(char c) const { |
| 1076 if (strict_quotes_) |
| 1077 return c == '"'; |
| 1078 return HttpUtil::IsQuote(c); |
| 1079 } |
| 1080 |
1001 } // namespace net | 1081 } // namespace net |
OLD | NEW |