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> |
| 8 #include <utility> |
7 #include "platform/loader/fetch/FetchUtils.h" | 9 #include "platform/loader/fetch/FetchUtils.h" |
8 #include "platform/network/HTTPParsers.h" | 10 #include "platform/network/HTTPParsers.h" |
9 #include "wtf/PtrUtil.h" | 11 #include "wtf/text/StringBuilder.h" |
10 #include <algorithm> | |
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 < m_headerList.size(); ++i) | 21 for (const auto& header : m_headerList) |
21 list->append(m_headerList[i]->first, m_headerList[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 m_headerList.push_back(WTF::wrapUnique(new Header(name.lower(), value))); | 35 // name. This reuses the casing of the name of the header already in the |
| 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 const auto& header = m_headerList.find(name); |
| 41 if (header != m_headerList.end()) |
| 42 m_headerList.insert(std::make_pair(header->first, value)); |
| 43 else |
| 44 m_headerList.insert(std::make_pair(name, value)); |
34 } | 45 } |
35 | 46 |
36 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 |
37 // "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 |
38 // these steps: | 50 // these steps: |
39 // 1. Byte lowercase |name|. | 51 // 1. If |list| contains |name|, then set the value of the first such header |
40 // 2. If there are any headers in |list| whose name is |name|, set the value | 52 // to |value| and remove the others. |
41 // 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 |
42 // 3. Otherwise, append a new header whose name is |name| and value is | 54 // |value| to |list|." |
43 // |value|, to |list|." | 55 const auto& existingHeader = m_headerList.find(name); |
44 const String lowercasedName = name.lower(); | 56 const FetchHeaderList::Header newHeader = std::make_pair( |
45 for (size_t i = 0; i < m_headerList.size(); ++i) { | 57 existingHeader != m_headerList.end() ? existingHeader->first : name, |
46 if (m_headerList[i]->first == lowercasedName) { | 58 value); |
47 m_headerList[i]->second = value; | 59 m_headerList.erase(name); |
48 for (size_t j = i + 1; j < m_headerList.size();) { | 60 m_headerList.insert(newHeader); |
49 if (m_headerList[j]->first == lowercasedName) | |
50 m_headerList.erase(j); | |
51 else | |
52 ++j; | |
53 } | |
54 return; | |
55 } | |
56 } | |
57 m_headerList.push_back(WTF::makeUnique<Header>(lowercasedName, value)); | |
58 } | 61 } |
59 | 62 |
60 String FetchHeaderList::extractMIMEType() const { | 63 String FetchHeaderList::extractMIMEType() const { |
61 // 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: |
62 // 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. |
63 String mimeType; | 66 String mimeType; |
64 if (!get("Content-Type", mimeType)) { | 67 if (!get("Content-Type", mimeType)) { |
65 // 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. |
66 return String(); | 69 return String(); |
67 } | 70 } |
68 // 3. Return MIMEType, byte lowercased. | 71 // 3. Return MIMEType, byte lowercased. |
69 return mimeType.lower(); | 72 return mimeType.lower(); |
70 } | 73 } |
71 | 74 |
72 size_t FetchHeaderList::size() const { | 75 size_t FetchHeaderList::size() const { |
73 return m_headerList.size(); | 76 return m_headerList.size(); |
74 } | 77 } |
75 | 78 |
76 void FetchHeaderList::remove(const String& name) { | 79 void FetchHeaderList::remove(const String& name) { |
77 // "To delete a name (|name|) from a header list (|list|), remove all headers | 80 // https://fetch.spec.whatwg.org/#concept-header-list-delete |
78 // whose name is |name|, byte lowercased, from |list|." | 81 // "To delete a name (name) from a header list (list), remove all headers |
79 const String lowercasedName = name.lower(); | 82 // whose name is a byte-case-insensitive match for name from list." |
80 for (size_t i = 0; i < m_headerList.size();) { | 83 m_headerList.erase(name); |
81 if (m_headerList[i]->first == lowercasedName) | |
82 m_headerList.erase(i); | |
83 else | |
84 ++i; | |
85 } | |
86 } | 84 } |
87 | 85 |
88 bool FetchHeaderList::get(const String& name, String& result) const { | 86 bool FetchHeaderList::get(const String& name, String& result) const { |
89 const String lowercasedName = name.lower(); | 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; |
90 bool found = false; | 95 bool found = false; |
91 for (const auto& header : m_headerList) { | 96 const auto& range = m_headerList.equal_range(name); |
92 if (header->first == lowercasedName) { | 97 for (auto header = range.first; header != range.second; ++header) { |
93 if (!found) { | 98 if (!found) { |
94 result = ""; | 99 resultBuilder.append(header->second); |
95 result.append(header->second); | 100 found = true; |
96 found = true; | 101 } else { |
97 } else { | 102 // TODO(rakuco): This must be ", " instead. crbug.com/700434. |
98 result.append(","); | 103 resultBuilder.append(','); |
99 result.append(header->second); | 104 resultBuilder.append(header->second); |
100 } | |
101 } | 105 } |
102 } | 106 } |
| 107 if (found) |
| 108 result = resultBuilder.toString(); |
103 return found; | 109 return found; |
104 } | 110 } |
105 | 111 |
| 112 // This is going to be removed: see crbug.com/645492. |
106 void FetchHeaderList::getAll(const String& name, Vector<String>& result) const { | 113 void FetchHeaderList::getAll(const String& name, Vector<String>& result) const { |
107 const String lowercasedName = name.lower(); | |
108 result.clear(); | 114 result.clear(); |
109 for (size_t i = 0; i < m_headerList.size(); ++i) { | 115 const auto& range = m_headerList.equal_range(name); |
110 if (m_headerList[i]->first == lowercasedName) | 116 for (auto header = range.first; header != range.second; ++header) { |
111 result.push_back(m_headerList[i]->second); | 117 result.push_back(header->second); |
112 } | 118 } |
113 } | 119 } |
114 | 120 |
115 bool FetchHeaderList::has(const String& name) const { | 121 bool FetchHeaderList::has(const String& name) const { |
116 const String lowercasedName = name.lower(); | 122 // https://fetch.spec.whatwg.org/#header-list-contains |
117 for (size_t i = 0; i < m_headerList.size(); ++i) { | 123 // "A header list (|list|) contains a name (|name|) if |list| contains a |
118 if (m_headerList[i]->first == lowercasedName) | 124 // header whose name is a byte-case-insensitive match for |name|." |
119 return true; | 125 return m_headerList.find(name) != m_headerList.end(); |
120 } | |
121 return false; | |
122 } | 126 } |
123 | 127 |
124 void FetchHeaderList::clearList() { | 128 void FetchHeaderList::clearList() { |
125 m_headerList.clear(); | 129 m_headerList.clear(); |
126 } | 130 } |
127 | 131 |
128 bool FetchHeaderList::containsNonSimpleHeader() const { | 132 bool FetchHeaderList::containsNonSimpleHeader() const { |
129 for (size_t i = 0; i < m_headerList.size(); ++i) { | 133 return std::any_of( |
130 if (!FetchUtils::isSimpleHeader(AtomicString(m_headerList[i]->first), | 134 m_headerList.begin(), m_headerList.end(), [](const Header& header) { |
131 AtomicString(m_headerList[i]->second))) | 135 return !FetchUtils::isSimpleHeader(AtomicString(header.first), |
132 return true; | 136 AtomicString(header.second)); |
133 } | 137 }); |
134 return false; | |
135 } | 138 } |
136 | 139 |
137 void FetchHeaderList::sortAndCombine() { | 140 Vector<FetchHeaderList::Header> FetchHeaderList::sortAndCombine() const { |
138 // https://fetch.spec.whatwg.org/#concept-header-list-sort-and-combine | 141 // https://fetch.spec.whatwg.org/#concept-header-list-sort-and-combine |
139 // "To sort and combine a header list..." | 142 // "To sort and combine a header list (|list|), run these steps: |
140 if (m_headerList.isEmpty()) | 143 // 1. Let |headers| be an empty list of name-value pairs with the key being |
141 return; | 144 // the name and value the value. |
142 | 145 // 2. Let |names| be all the names of the headers in |list|, byte-lowercased, |
143 std::sort( | 146 // with duplicates removed, and finally sorted lexicographically. |
144 m_headerList.begin(), m_headerList.end(), | 147 // 3. For each |name| in |names|, run these substeps: |
145 [](const std::unique_ptr<Header>& a, const std::unique_ptr<Header>& b) { | 148 // 1. Let |value| be the combined value given |name| and |list|. |
146 return WTF::codePointCompareLessThan(a->first, b->first); | 149 // 2. Append |name-value| to |headers|. |
147 }); | 150 // 4. Return |headers|." |
148 | 151 Vector<FetchHeaderList::Header> ret; |
149 for (size_t index = m_headerList.size() - 1; index > 0; --index) { | 152 for (auto it = m_headerList.begin(); it != m_headerList.end();) { |
150 if (m_headerList[index - 1]->first == m_headerList[index]->first) { | 153 const String& headerName = it->first.lower(); |
151 m_headerList[index - 1]->second.append(","); | 154 String combinedValue; |
152 m_headerList[index - 1]->second.append(m_headerList[index]->second); | 155 get(headerName, combinedValue); |
153 m_headerList.erase(index, 1); | 156 ret.emplace_back(std::make_pair(headerName, combinedValue)); |
154 } | 157 // Skip to the next distinct key. |
| 158 it = m_headerList.upper_bound(headerName); |
155 } | 159 } |
| 160 return ret; |
156 } | 161 } |
157 | 162 |
158 bool FetchHeaderList::isValidHeaderName(const String& name) { | 163 bool FetchHeaderList::isValidHeaderName(const String& name) { |
159 // "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 |
160 // token production." | 165 // token production." |
161 return isValidHTTPToken(name); | 166 return isValidHTTPToken(name); |
162 } | 167 } |
163 | 168 |
164 bool FetchHeaderList::isValidHeaderValue(const String& value) { | 169 bool FetchHeaderList::isValidHeaderValue(const String& value) { |
165 // "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 |
166 // and contains no 0x0A or 0x0D bytes." | 171 // and contains no 0x0A or 0x0D bytes." |
167 return isValidHTTPHeaderValue(value); | 172 return isValidHTTPHeaderValue(value); |
168 } | 173 } |
169 | 174 |
170 } // namespace blink | 175 } // namespace blink |
OLD | NEW |