| 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 |