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 |