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 3 // found in the LICENSE file. 4 5 #include "config.h" 6 #include "modules/serviceworkers/Headers.h" 7 8 #include "bindings/core/v8/Dictionary.h" 9 #include "bindings/core/v8/ExceptionState.h" 10 #include "core/fetch/FetchUtils.h" 11 #include "core/xml/XMLHttpRequest.h" 12 #include "modules/serviceworkers/HeadersForEachCallback.h" 13 #include "wtf/NotFound.h" 14 #include "wtf/PassRefPtr.h" 15 #include "wtf/RefPtr.h" 16 #include "wtf/text/WTFString.h" 17 18 namespace blink { 19 20 Headers* Headers::create() 21 { 22 return new Headers; 23 } 24 25 Headers* Headers::create(ExceptionState&) 26 { 27 return create(); 28 } 29 30 Headers* Headers::create(const Headers* init, ExceptionState& exceptionState) 31 { 32 // "The Headers(|init|) constructor, when invoked, must run these steps:" 33 // "1. Let |headers| be a new Headers object." 34 Headers* headers = create(); 35 // "2. If |init| is given, fill headers with |init|. Rethrow any exception." 36 headers->fillWith(init, exceptionState); 37 // "3. Return |headers|." 38 return headers; 39 } 40 41 Headers* Headers::create(const Dictionary& init, ExceptionState& exceptionState) 42 { 43 // "The Headers(|init|) constructor, when invoked, must run these steps:" 44 // "1. Let |headers| be a new Headers object." 45 Headers* headers = create(); 46 // "2. If |init| is given, fill headers with |init|. Rethrow any exception." 47 headers->fillWith(init, exceptionState); 48 // "3. Return |headers|." 49 return headers; 50 } 51 52 Headers* Headers::create(FetchHeaderList* headerList) 53 { 54 return new Headers(headerList); 55 } 56 57 Headers* Headers::createCopy() const 58 { 59 FetchHeaderList* headerList = m_headerList->createCopy(); 60 Headers* headers = create(headerList); 61 headers->m_guard = m_guard; 62 return headers; 63 } 64 65 unsigned long Headers::size() const 66 { 67 return m_headerList->size(); 68 } 69 70 void Headers::append(const String& name, const String& value, ExceptionState& exceptionState) 71 { 72 // "To append a name/value (|name|/|value|) pair to a Headers object 73 // (|headers|), run these steps:" 74 // "1. If |name| is not a name or |value| is not a value, throw a 75 // TypeError." 76 if (!FetchHeaderList::isValidHeaderName(name)) { 77 exceptionState.throwTypeError("Invalid name"); 78 return; 79 } 80 if (!FetchHeaderList::isValidHeaderValue(value)) { 81 exceptionState.throwTypeError("Invalid value"); 82 return; 83 } 84 // "2. If guard is |request|, throw a TypeError." 85 if (m_guard == ImmutableGuard) { 86 exceptionState.throwTypeError("Headers are immutable"); 87 return; 88 } 89 // "3. Otherwise, if guard is |request| and |name| is a forbidden header 90 // name, return." 91 if (m_guard == RequestGuard && FetchUtils::isForbiddenHeaderName(name)) 92 return; 93 // "4. Otherwise, if guard is |request-no-CORS| and |name|/|value| is not a 94 // simple header, return." 95 if (m_guard == RequestNoCORSGuard && !FetchUtils::isSimpleHeader(AtomicString(name), AtomicString(value))) 96 return; 97 // "5. Otherwise, if guard is |response| and |name| is a forbidden response 98 // header name, return." 99 if (m_guard == ResponseGuard && FetchUtils::isForbiddenResponseHeaderName(name)) 100 return; 101 // "6. Append |name|/|value| to header list." 102 m_headerList->append(name, value); 103 } 104 105 void Headers::remove(const String& name, ExceptionState& exceptionState) 106 { 107 // "The delete(|name|) method, when invoked, must run these steps:" 108 // "1. If name is not a name, throw a TypeError." 109 if (!FetchHeaderList::isValidHeaderName(name)) { 110 exceptionState.throwTypeError("Invalid name"); 111 return; 112 } 113 // "2. If guard is |immutable|, throw a TypeError." 114 if (m_guard == ImmutableGuard) { 115 exceptionState.throwTypeError("Headers are immutable"); 116 return; 117 } 118 // "3. Otherwise, if guard is |request| and |name| is a forbidden header 119 // name, return." 120 if (m_guard == RequestGuard && FetchUtils::isForbiddenHeaderName(name)) 121 return; 122 // "4. Otherwise, if guard is |request-no-CORS| and |name|/`invalid` is not 123 // a simple header, return." 124 if (m_guard == RequestNoCORSGuard && !FetchUtils::isSimpleHeader(AtomicString(name), "invalid")) 125 return; 126 // "5. Otherwise, if guard is |response| and |name| is a forbidden response 127 // header name, return." 128 if (m_guard == ResponseGuard && FetchUtils::isForbiddenResponseHeaderName(name)) 129 return; 130 // "6. Delete |name| from header list." 131 m_headerList->remove(name); 132 } 133 134 String Headers::get(const String& name, ExceptionState& exceptionState) 135 { 136 // "The get(|name|) method, when invoked, must run these steps:" 137 // "1. If |name| is not a name, throw a TypeError." 138 if (!FetchHeaderList::isValidHeaderName(name)) { 139 exceptionState.throwTypeError("Invalid name"); 140 return String(); 141 } 142 // "2. Return the value of the first header in header list whose name is 143 // |name|, and null otherwise." 144 String result; 145 m_headerList->get(name, result); 146 return result; 147 } 148 149 Vector<String> Headers::getAll(const String& name, ExceptionState& exceptionState) 150 { 151 // "The getAll(|name|) method, when invoked, must run these steps:" 152 // "1. If |name| is not a name, throw a TypeError." 153 if (!FetchHeaderList::isValidHeaderName(name)) { 154 exceptionState.throwTypeError("Invalid name"); 155 return Vector<String>(); 156 } 157 // "2. Return the values of all headers in header list whose name is |name|, 158 // in list order, and the empty sequence otherwise." 159 Vector<String> result; 160 m_headerList->getAll(name, result); 161 return result; 162 } 163 164 bool Headers::has(const String& name, ExceptionState& exceptionState) 165 { 166 // "The has(|name|) method, when invoked, must run these steps:" 167 // "1. If |name| is not a name, throw a TypeError." 168 if (!FetchHeaderList::isValidHeaderName(name)) { 169 exceptionState.throwTypeError("Invalid name"); 170 return false; 171 } 172 // "2. Return true if there is a header in header list whose name is |name|, 173 // and false otherwise." 174 return m_headerList->has(name); 175 } 176 177 void Headers::set(const String& name, const String& value, ExceptionState& exceptionState) 178 { 179 // "The set(|name|, |value|) method, when invoked, must run these steps:" 180 // "1. If |name| is not a name or |value| is not a value, throw a 181 // TypeError." 182 if (!FetchHeaderList::isValidHeaderName(name)) { 183 exceptionState.throwTypeError("Invalid name"); 184 return; 185 } 186 if (!FetchHeaderList::isValidHeaderValue(value)) { 187 exceptionState.throwTypeError("Invalid value"); 188 return; 189 } 190 // "2. If guard is |immutable|, throw a TypeError." 191 if (m_guard == ImmutableGuard) { 192 exceptionState.throwTypeError("Headers are immutable"); 193 return; 194 } 195 // "3. Otherwise, if guard is |request| and |name| is a forbidden header 196 // name, return." 197 if (m_guard == RequestGuard && FetchUtils::isForbiddenHeaderName(name)) 198 return; 199 // "4. Otherwise, if guard is |request-no-CORS| and |name|/|value| is not a 200 // simple header, return." 201 if (m_guard == RequestNoCORSGuard && !FetchUtils::isSimpleHeader(AtomicString(name), AtomicString(value))) 202 return; 203 // "5. Otherwise, if guard is |response| and |name| is a forbidden response 204 // header name, return." 205 if (m_guard == ResponseGuard && FetchUtils::isForbiddenResponseHeaderName(name)) 206 return; 207 // "6. Set |name|/|value| in header list." 208 m_headerList->set(name, value); 209 } 210 211 void Headers::forEach(HeadersForEachCallback* callback, const ScriptValue& thisArg) 212 { 213 forEachInternal(callback, &thisArg); 214 } 215 216 void Headers::forEach(HeadersForEachCallback* callback) 217 { 218 forEachInternal(callback, 0); 219 } 220 221 void Headers::fillWith(const Headers* object, ExceptionState& exceptionState) 222 { 223 ASSERT(m_headerList->size() == 0); 224 // "To fill a Headers object (|this|) with a given object (|object|), run 225 // these steps:" 226 // "1. If |object| is a Headers object, copy its header list as 227 // |headerListCopy| and then for each |header| in |headerListCopy|, 228 // retaining order, append header's |name|/|header|'s value to 229 // |headers|. Rethrow any exception." 230 for (size_t i = 0; i < object->m_headerList->list().size(); ++i) { 231 append(object->m_headerList->list()[i]->first, object->m_headerList->list()[i]->second, exceptionState); 232 if (exceptionState.hadException()) 233 return; 234 } 235 } 236 237 void Headers::fillWith(const Dictionary& object, ExceptionState& exceptionState) 238 { 239 ASSERT(m_headerList->size() == 0); 240 Vector<String> keys; 241 object.getOwnPropertyNames(keys); 242 if (keys.size() == 0) 243 return; 244 245 // Because of the restrictions in IDL compiler of blink we recieve 246 // sequence<sequence<ByteString>> as a Dictionary, which is a type of union 247 // type of HeadersInit defined in the spec. 248 // http://fetch.spec.whatwg.org/#headers-class 249 // FIXME: Support sequence<sequence<ByteString>>. 250 Vector<String> keyValuePair; 251 if (DictionaryHelper::get(object, keys[0], keyValuePair)) { 252 // "2. Otherwise, if |object| is a sequence, then for each |header| in 253 // |object|, run these substeps: 254 // 1. If |header| does not contain exactly two items, throw a 255 // TypeError. 256 // 2. Append |header|'s first item/|header|'s second item to 257 // |headers|. Rethrow any exception." 258 for (size_t i = 0; i < keys.size(); ++i) { 259 // We've already got the keyValuePair for key[0]. 260 if (i > 0) { 261 if (!DictionaryHelper::get(object, keys[i], keyValuePair)) { 262 exceptionState.throwTypeError("Invalid value"); 263 return; 264 } 265 } 266 if (keyValuePair.size() != 2) { 267 exceptionState.throwTypeError("Invalid value"); 268 return; 269 } 270 append(keyValuePair[0], keyValuePair[1], exceptionState); 271 if (exceptionState.hadException()) 272 return; 273 keyValuePair.clear(); 274 } 275 return; 276 } 277 // "3. Otherwise, if |object| is an open-ended dictionary, then for each 278 // |header| in object, run these substeps: 279 // 1. Set |header|'s key to |header|'s key, converted to ByteString. 280 // Rethrow any exception. 281 // 2. Append |header|'s key/|header|'s value to |headers|. Rethrow any 282 // exception." 283 // FIXME: Support OpenEndedDictionary<ByteString>. 284 for (size_t i = 0; i < keys.size(); ++i) { 285 String value; 286 if (!DictionaryHelper::get(object, keys[i], value)) { 287 exceptionState.throwTypeError("Invalid value"); 288 return; 289 } 290 append(keys[i], value, exceptionState); 291 if (exceptionState.hadException()) 292 return; 293 } 294 } 295 296 Headers::Headers() 297 : m_headerList(FetchHeaderList::create()) 298 , m_guard(NoneGuard) 299 { 300 } 301 302 Headers::Headers(FetchHeaderList* headerList) 303 : m_headerList(headerList) 304 , m_guard(NoneGuard) 305 { 306 } 307 308 void Headers::forEachInternal(HeadersForEachCallback* callback, const ScriptValue* thisArg) 309 { 310 TrackExceptionState exceptionState; 311 for (size_t i = 0; i < m_headerList->size(); ++i) { 312 if (thisArg) 313 callback->handleItem(*thisArg, m_headerList->list()[i]->second, m_headerList->list()[i]->first, this); 314 else 315 callback->handleItem(m_headerList->list()[i]->second, m_headerList->list()[i]->first, this); 316 if (exceptionState.hadException()) 317 break; 318 } 319 } 320 321 void Headers::trace(Visitor* visitor) 322 { 323 visitor->trace(m_headerList); 324 } 325 326 } // namespace blink 327