| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 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 #include "modules/fetch/FetchHeaderList.h" | 5 #include "modules/fetch/FetchHeaderList.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 #include <utility> |
| 8 #include "platform/loader/fetch/FetchUtils.h" | 9 #include "platform/loader/fetch/FetchUtils.h" |
| 9 #include "platform/network/HTTPParsers.h" | 10 #include "platform/network/HTTPParsers.h" |
| 10 #include "platform/wtf/PtrUtil.h" | 11 #include "platform/wtf/text/StringBuilder.h" |
| 11 | 12 |
| 12 namespace blink { | 13 namespace blink { |
| 13 | 14 |
| 14 FetchHeaderList* FetchHeaderList::Create() { | 15 FetchHeaderList* FetchHeaderList::Create() { |
| 15 return new FetchHeaderList(); | 16 return new FetchHeaderList(); |
| 16 } | 17 } |
| 17 | 18 |
| 18 FetchHeaderList* FetchHeaderList::Clone() const { | 19 FetchHeaderList* FetchHeaderList::Clone() const { |
| 19 FetchHeaderList* list = Create(); | 20 FetchHeaderList* list = Create(); |
| 20 for (size_t i = 0; i < header_list_.size(); ++i) | 21 for (const auto& header : header_list_) |
| 21 list->Append(header_list_[i]->first, header_list_[i]->second); | 22 list->Append(header.first, header.second); |
| 22 return list; | 23 return list; |
| 23 } | 24 } |
| 24 | 25 |
| 25 FetchHeaderList::FetchHeaderList() {} | 26 FetchHeaderList::FetchHeaderList() {} |
| 26 | 27 |
| 27 FetchHeaderList::~FetchHeaderList() {} | 28 FetchHeaderList::~FetchHeaderList() {} |
| 28 | 29 |
| 29 void FetchHeaderList::Append(const String& name, const String& value) { | 30 void FetchHeaderList::Append(const String& name, const String& value) { |
| 31 // https://fetch.spec.whatwg.org/#concept-header-list-append |
| 30 // "To append a name/value (|name|/|value|) pair to a header list (|list|), | 32 // "To append a name/value (|name|/|value|) pair to a header list (|list|), |
| 31 // append a new header whose name is |name|, byte lowercased, and value is | 33 // run these steps: |
| 32 // |value|, to |list|." | 34 // 1. If |list| contains |name|, then set |name| to the first such header’s |
| 33 header_list_.push_back( | 35 // name. This reuses the casing of the name of the header already in the |
| 34 WTF::WrapUnique(new Header(name.DeprecatedLower(), value))); | 36 // header list, if any. If there are multiple matched headers their names |
| 37 // will all be identical. |
| 38 // 2. Append a new header whose name is |name| and |value| is |value| to |
| 39 // |list|." |
| 40 auto header = header_list_.find(name); |
| 41 if (header != header_list_.end()) |
| 42 header_list_.insert(std::make_pair(header->first, value)); |
| 43 else |
| 44 header_list_.insert(std::make_pair(name, value)); |
| 35 } | 45 } |
| 36 | 46 |
| 37 void FetchHeaderList::Set(const String& name, const String& value) { | 47 void FetchHeaderList::Set(const String& name, const String& value) { |
| 48 // https://fetch.spec.whatwg.org/#concept-header-list-set |
| 38 // "To set a name/value (|name|/|value|) pair in a header list (|list|), run | 49 // "To set a name/value (|name|/|value|) pair in a header list (|list|), run |
| 39 // these steps: | 50 // these steps: |
| 40 // 1. Byte lowercase |name|. | 51 // 1. If |list| contains |name|, then set the value of the first such header |
| 41 // 2. If there are any headers in |list| whose name is |name|, set the value | 52 // to |value| and remove the others. |
| 42 // of the first such header to |value| and remove the others. | 53 // 2. Otherwise, append a new header whose name is |name| and value is |
| 43 // 3. Otherwise, append a new header whose name is |name| and value is | 54 // |value| to |list|." |
| 44 // |value|, to |list|." | 55 auto existingHeader = header_list_.find(name); |
| 45 const String lowercased_name = name.DeprecatedLower(); | 56 const FetchHeaderList::Header newHeader = std::make_pair( |
| 46 for (size_t i = 0; i < header_list_.size(); ++i) { | 57 existingHeader != header_list_.end() ? existingHeader->first : name, |
| 47 if (header_list_[i]->first == lowercased_name) { | 58 value); |
| 48 header_list_[i]->second = value; | 59 header_list_.erase(name); |
| 49 for (size_t j = i + 1; j < header_list_.size();) { | 60 header_list_.insert(newHeader); |
| 50 if (header_list_[j]->first == lowercased_name) | |
| 51 header_list_.erase(j); | |
| 52 else | |
| 53 ++j; | |
| 54 } | |
| 55 return; | |
| 56 } | |
| 57 } | |
| 58 header_list_.push_back(WTF::MakeUnique<Header>(lowercased_name, value)); | |
| 59 } | 61 } |
| 60 | 62 |
| 61 String FetchHeaderList::ExtractMIMEType() const { | 63 String FetchHeaderList::ExtractMIMEType() const { |
| 62 // To extract a MIME type from a header list (headers), run these steps: | 64 // To extract a MIME type from a header list (headers), run these steps: |
| 63 // 1. Let MIMEType be the result of parsing `Content-Type` in headers. | 65 // 1. Let MIMEType be the result of parsing `Content-Type` in headers. |
| 64 String mime_type; | 66 String mime_type; |
| 65 if (!Get("Content-Type", mime_type)) { | 67 if (!Get("Content-Type", mime_type)) { |
| 66 // 2. If MIMEType is null or failure, return the empty byte sequence. | 68 // 2. If MIMEType is null or failure, return the empty byte sequence. |
| 67 return String(); | 69 return String(); |
| 68 } | 70 } |
| 69 // 3. Return MIMEType, byte lowercased. | 71 // 3. Return MIMEType, byte lowercased. |
| 70 return mime_type.DeprecatedLower(); | 72 return mime_type.DeprecatedLower(); |
| 71 } | 73 } |
| 72 | 74 |
| 73 size_t FetchHeaderList::size() const { | 75 size_t FetchHeaderList::size() const { |
| 74 return header_list_.size(); | 76 return header_list_.size(); |
| 75 } | 77 } |
| 76 | 78 |
| 77 void FetchHeaderList::Remove(const String& name) { | 79 void FetchHeaderList::Remove(const String& name) { |
| 78 // "To delete a name (|name|) from a header list (|list|), remove all headers | 80 // https://fetch.spec.whatwg.org/#concept-header-list-delete |
| 79 // whose name is |name|, byte lowercased, from |list|." | 81 // "To delete a name (name) from a header list (list), remove all headers |
| 80 const String lowercased_name = name.DeprecatedLower(); | 82 // whose name is a byte-case-insensitive match for name from list." |
| 81 for (size_t i = 0; i < header_list_.size();) { | 83 header_list_.erase(name); |
| 82 if (header_list_[i]->first == lowercased_name) | |
| 83 header_list_.erase(i); | |
| 84 else | |
| 85 ++i; | |
| 86 } | |
| 87 } | 84 } |
| 88 | 85 |
| 89 bool FetchHeaderList::Get(const String& name, String& result) const { | 86 bool FetchHeaderList::Get(const String& name, String& result) const { |
| 90 const String lowercased_name = name.DeprecatedLower(); | 87 // https://fetch.spec.whatwg.org/#concept-header-list-combine |
| 88 // "To combine a name/value (|name|/|value|) pair in a header list (|list|), |
| 89 // run these steps: |
| 90 // 1. If |list| contains |name|, then set the value of the first such header |
| 91 // to its value, followed by 0x2C 0x20, followed by |value|. |
| 92 // 2. Otherwise, append a new header whose name is |name| and value is |
| 93 // |value| to |list|." |
| 94 StringBuilder resultBuilder; |
| 91 bool found = false; | 95 bool found = false; |
| 92 for (const auto& header : header_list_) { | 96 auto range = header_list_.equal_range(name); |
| 93 if (header->first == lowercased_name) { | 97 for (auto header = range.first; header != range.second; ++header) { |
| 94 if (!found) { | 98 if (!found) { |
| 95 result = ""; | 99 resultBuilder.Append(header->second); |
| 96 result.Append(header->second); | 100 found = true; |
| 97 found = true; | 101 } else { |
| 98 } else { | 102 // TODO(rakuco): This must be ", " instead. crbug.com/700434. |
| 99 result.Append(","); | 103 resultBuilder.Append(','); |
| 100 result.Append(header->second); | 104 resultBuilder.Append(header->second); |
| 101 } | |
| 102 } | 105 } |
| 103 } | 106 } |
| 107 if (found) |
| 108 result = resultBuilder.ToString(); |
| 104 return found; | 109 return found; |
| 105 } | 110 } |
| 106 | 111 |
| 112 // This is going to be removed: see crbug.com/645492. |
| 107 void FetchHeaderList::GetAll(const String& name, Vector<String>& result) const { | 113 void FetchHeaderList::GetAll(const String& name, Vector<String>& result) const { |
| 108 const String lowercased_name = name.DeprecatedLower(); | |
| 109 result.Clear(); | 114 result.Clear(); |
| 110 for (size_t i = 0; i < header_list_.size(); ++i) { | 115 auto range = header_list_.equal_range(name); |
| 111 if (header_list_[i]->first == lowercased_name) | 116 for (auto header = range.first; header != range.second; ++header) { |
| 112 result.push_back(header_list_[i]->second); | 117 result.push_back(header->second); |
| 113 } | 118 } |
| 114 } | 119 } |
| 115 | 120 |
| 116 bool FetchHeaderList::Has(const String& name) const { | 121 bool FetchHeaderList::Has(const String& name) const { |
| 117 const String lowercased_name = name.DeprecatedLower(); | 122 // https://fetch.spec.whatwg.org/#header-list-contains |
| 118 for (size_t i = 0; i < header_list_.size(); ++i) { | 123 // "A header list (|list|) contains a name (|name|) if |list| contains a |
| 119 if (header_list_[i]->first == lowercased_name) | 124 // header whose name is a byte-case-insensitive match for |name|." |
| 120 return true; | 125 return header_list_.find(name) != header_list_.end(); |
| 121 } | |
| 122 return false; | |
| 123 } | 126 } |
| 124 | 127 |
| 125 void FetchHeaderList::ClearList() { | 128 void FetchHeaderList::ClearList() { |
| 126 header_list_.Clear(); | 129 header_list_.clear(); |
| 127 } | 130 } |
| 128 | 131 |
| 129 bool FetchHeaderList::ContainsNonSimpleHeader() const { | 132 bool FetchHeaderList::ContainsNonSimpleHeader() const { |
| 130 for (size_t i = 0; i < header_list_.size(); ++i) { | 133 return std::any_of( |
| 131 if (!FetchUtils::IsSimpleHeader(AtomicString(header_list_[i]->first), | 134 header_list_.cbegin(), header_list_.cend(), [](const Header& header) { |
| 132 AtomicString(header_list_[i]->second))) | 135 return !FetchUtils::IsSimpleHeader(AtomicString(header.first), |
| 133 return true; | 136 AtomicString(header.second)); |
| 134 } | 137 }); |
| 135 return false; | |
| 136 } | 138 } |
| 137 | 139 |
| 138 void FetchHeaderList::SortAndCombine() { | 140 Vector<FetchHeaderList::Header> FetchHeaderList::SortAndCombine() const { |
| 139 // https://fetch.spec.whatwg.org/#concept-header-list-sort-and-combine | 141 // https://fetch.spec.whatwg.org/#concept-header-list-sort-and-combine |
| 140 // "To sort and combine a header list..." | 142 // "To sort and combine a header list (|list|), run these steps: |
| 141 if (header_list_.IsEmpty()) | 143 // 1. Let |headers| be an empty list of name-value pairs with the key being |
| 142 return; | 144 // the name and value the value. |
| 143 | 145 // 2. Let |names| be all the names of the headers in |list|, byte-lowercased, |
| 144 std::sort( | 146 // with duplicates removed, and finally sorted lexicographically. |
| 145 header_list_.begin(), header_list_.end(), | 147 // 3. For each |name| in |names|, run these substeps: |
| 146 [](const std::unique_ptr<Header>& a, const std::unique_ptr<Header>& b) { | 148 // 1. Let |value| be the combined value given |name| and |list|. |
| 147 return WTF::CodePointCompareLessThan(a->first, b->first); | 149 // 2. Append |name-value| to |headers|. |
| 148 }); | 150 // 4. Return |headers|." |
| 149 | 151 Vector<FetchHeaderList::Header> ret; |
| 150 for (size_t index = header_list_.size() - 1; index > 0; --index) { | 152 for (auto it = header_list_.cbegin(); it != header_list_.cend();) { |
| 151 if (header_list_[index - 1]->first == header_list_[index]->first) { | 153 const String& headerName = it->first.LowerASCII(); |
| 152 header_list_[index - 1]->second.Append(","); | 154 String combinedValue; |
| 153 header_list_[index - 1]->second.Append(header_list_[index]->second); | 155 Get(headerName, combinedValue); |
| 154 header_list_.erase(index, 1); | 156 ret.emplace_back(std::make_pair(headerName, combinedValue)); |
| 155 } | 157 // Skip to the next distinct key. |
| 158 it = header_list_.upper_bound(headerName); |
| 156 } | 159 } |
| 160 return ret; |
| 157 } | 161 } |
| 158 | 162 |
| 159 bool FetchHeaderList::IsValidHeaderName(const String& name) { | 163 bool FetchHeaderList::IsValidHeaderName(const String& name) { |
| 160 // "A name is a case-insensitive byte sequence that matches the field-name | 164 // "A name is a case-insensitive byte sequence that matches the field-name |
| 161 // token production." | 165 // token production." |
| 162 return IsValidHTTPToken(name); | 166 return IsValidHTTPToken(name); |
| 163 } | 167 } |
| 164 | 168 |
| 165 bool FetchHeaderList::IsValidHeaderValue(const String& value) { | 169 bool FetchHeaderList::IsValidHeaderValue(const String& value) { |
| 166 // "A value is a byte sequence that matches the field-value token production | 170 // "A value is a byte sequence that matches the field-value token production |
| 167 // and contains no 0x0A or 0x0D bytes." | 171 // and contains no 0x0A or 0x0D bytes." |
| 168 return IsValidHTTPHeaderValue(value); | 172 return IsValidHTTPHeaderValue(value); |
| 169 } | 173 } |
| 170 | 174 |
| 171 } // namespace blink | 175 } // namespace blink |
| OLD | NEW |