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 "Request.h" 7 8 #include "bindings/core/v8/Dictionary.h" 9 #include "core/dom/ExecutionContext.h" 10 #include "core/fetch/FetchUtils.h" 11 #include "core/fetch/ResourceLoaderOptions.h" 12 #include "core/loader/ThreadableLoader.h" 13 #include "core/xml/XMLHttpRequest.h" 14 #include "modules/serviceworkers/FetchManager.h" 15 #include "modules/serviceworkers/HeadersForEachCallback.h" 16 #include "modules/serviceworkers/RequestInit.h" 17 #include "platform/NotImplemented.h" 18 #include "platform/network/HTTPParsers.h" 19 #include "platform/network/ResourceRequest.h" 20 #include "platform/weborigin/Referrer.h" 21 #include "public/platform/WebServiceWorkerRequest.h" 22 23 namespace blink { 24 25 namespace { 26 27 class FillWebRequestHeaders : public HeadersForEachCallback { 28 public: 29 FillWebRequestHeaders(WebServiceWorkerRequest* webRequest) : m_webRequest(webRequest) { } 30 31 virtual bool handleItem(ScriptValue, const String&, const String&, Headers*) 32 { 33 ASSERT_NOT_REACHED(); 34 return false; 35 } 36 37 virtual bool handleItem(const String& value, const String& key, Headers*) 38 { 39 m_webRequest->appendHeader(key, value); 40 return true; 41 } 42 43 private: 44 WebServiceWorkerRequest* m_webRequest; 45 }; 46 47 } // namespace 48 49 Request* Request::createRequestWithRequestData(ExecutionContext* context, FetchRequestData* request, const RequestInit& init, FetchRequestData::Mode mode, FetchRequestData::Credentials credentials, ExceptionState& exceptionState) 50 { 51 // "7. Let |mode| be |init|'s mode member if it is present, and 52 // |fallbackMode| otherwise." 53 // "8. If |mode| is non-null, set |request|'s mode to |mode|." 54 if (init.mode == "same-origin") { 55 request->setMode(FetchRequestData::SameOriginMode); 56 } else if (init.mode == "no-cors") { 57 request->setMode(mode = FetchRequestData::NoCORSMode); 58 } else if (init.mode == "cors") { 59 request->setMode(FetchRequestData::CORSMode); 60 } else { 61 // Instead of using null as a special fallback value, we pass the 62 // current mode in Request::create(). So we just set here. 63 request->setMode(mode); 64 } 65 66 // "9. Let |credentials| be |init|'s credentials member if it is present, 67 // and |fallbackCredentials| otherwise." 68 // "10. If |credentials| is non-null, set |request|'s credentials mode to 69 // |credentials|. 70 if (init.credentials == "omit") { 71 request->setCredentials(FetchRequestData::OmitCredentials); 72 } else if (init.credentials == "same-origin") { 73 request->setCredentials(FetchRequestData::SameOriginCredentials); 74 } else if (init.credentials == "include") { 75 request->setCredentials(FetchRequestData::IncludeCredentials); 76 } else { 77 // Instead of using null as a special fallback value, we pass the 78 // current credentials in Request::create(). So we just set here. 79 request->setCredentials(credentials); 80 } 81 82 // "11. If |init|'s method member is present, let |method| be it and run 83 // these substeps:" 84 if (!init.method.isEmpty()) { 85 // "1. If |method| is not a useful method, throw a TypeError." 86 if (!FetchUtils::isUsefulMethod(init.method)) { 87 exceptionState.throwTypeError("'" + init.method + "' HTTP method is unsupported."); 88 return 0; 89 } 90 if (!isValidHTTPToken(init.method)) { 91 exceptionState.throwTypeError("'" + init.method + "' is not a valid HTTP method."); 92 return 0; 93 } 94 // FIXME: "2. Add case correction as in XMLHttpRequest?" 95 // "3. Set |request|'s method to |method|." 96 request->setMethod(XMLHttpRequest::uppercaseKnownHTTPMethod(AtomicString(init.method))); 97 } 98 // "12. Let |r| be a new Request object associated with |request|, Headers 99 // object." 100 Request* r = Request::create(context, request); 101 102 // "13. Let |headers| be a copy of |r|'s Headers object." 103 // "14. If |init|'s headers member is present, set |headers| to |init|'s 104 // headers member." 105 // We don't create a copy of r's Headers object when init's headers member 106 // is present. 107 Headers* headers = 0; 108 if (!init.headers && init.headersDictionary.isUndefinedOrNull()) { 109 headers = r->headers()->createCopy(); 110 } 111 // "15. Empty |r|'s request's header list." 112 r->clearHeaderList(); 113 114 // "16. If |r|'s request's mode is no CORS, run these substeps: 115 if (r->request()->mode() == FetchRequestData::NoCORSMode) { 116 // "1. If |r|'s request's method is not a simple method, throw a 117 // TypeError." 118 if (!FetchUtils::isSimpleMethod(r->request()->method())) { 119 exceptionState.throwTypeError("'" + r->request()->method() + "' is unsupported in no-cors mode."); 120 return 0; 121 } 122 // "Set |r|'s Headers object's guard to |request-no-CORS|. 123 r->headers()->setGuard(Headers::RequestNoCORSGuard); 124 } 125 126 // "17. Fill |r|'s Headers object with |headers|. Rethrow any exceptions." 127 if (init.headers) { 128 ASSERT(init.headersDictionary.isUndefinedOrNull()); 129 r->headers()->fillWith(init.headers.get(), exceptionState); 130 } else if (!init.headersDictionary.isUndefinedOrNull()) { 131 r->headers()->fillWith(init.headersDictionary, exceptionState); 132 } else { 133 ASSERT(headers); 134 r->headers()->fillWith(headers, exceptionState); 135 } 136 if (exceptionState.hadException()) 137 return 0; 138 // "18. If |init|'s body member is present, run these substeps:" 139 if (init.bodyBlobHandle) { 140 // "1. Let |stream| and |Content-Type| be the result of extracting 141 // |init|'s body member." 142 // "2. Set |r|'s request's body to |stream|." 143 // "3.If |Content-Type| is non-null and |r|'s request's header list 144 // contains no header named `Content-Type`, append 145 // `Content-Type`/|Content-Type| to |r|'s Headers object. Rethrow any 146 // exception." 147 r->setBodyBlobHandle(init.bodyBlobHandle); 148 if (!init.bodyBlobHandle->type().isEmpty() && !r->headers()->has("Content-Type", exceptionState)) { 149 r->headers()->append("Content-Type", init.bodyBlobHandle->type(), exceptionState); 150 } 151 if (exceptionState.hadException()) 152 return 0; 153 } 154 // "19. Set |r|'s MIME type to the result of extracting a MIME type from 155 // |r|'s request's header list." 156 // FIXME: We don't have MIME type in Request object yet. 157 158 // "20. Return |r|." 159 return r; 160 } 161 162 Request* Request::create(ExecutionContext* context, const String& input, ExceptionState& exceptionState) 163 { 164 return create(context, input, Dictionary(), exceptionState); 165 } 166 167 Request* Request::create(ExecutionContext* context, const String& input, const Dictionary& init, ExceptionState& exceptionState) 168 { 169 // "2. Let |request| be |input|'s associated request, if |input| is a 170 // Request object, and a new request otherwise." 171 FetchRequestData* request(FetchRequestData::create(context)); 172 // "3. Set |request| to a restricted copy of itself." 173 request = request->createRestrictedCopy(context, SecurityOrigin::create(context->url())); 174 // "6. If |input| is a string, run these substeps:" 175 // "1. Let |parsedURL| be the result of parsing |input| with entry settings 176 // object's API base URL." 177 KURL parsedURL = context->completeURL(input); 178 // "2. If |parsedURL| is failure, throw a TypeError." 179 if (!parsedURL.isValid()) { 180 exceptionState.throwTypeError("Invalid URL"); 181 return 0; 182 } 183 // "3. Set |request|'s url to |parsedURL|." 184 request->setURL(parsedURL); 185 // "4. Set |fallbackMode| to CORS." 186 // "5. Set |fallbackCredentials| to omit." 187 return createRequestWithRequestData(context, request, RequestInit(context, init, exceptionState), FetchRequestData::CORSMode, FetchRequestData::OmitCredentials, exceptionState); 188 } 189 190 Request* Request::create(ExecutionContext* context, Request* input, ExceptionState& exceptionState) 191 { 192 return create(context, input, Dictionary(), exceptionState); 193 } 194 195 Request* Request::create(ExecutionContext* context, Request* input, const Dictionary& init, ExceptionState& exceptionState) 196 { 197 // "1. If input is a Request object, run these substeps:" 198 // " 1. If input's used flag is set, throw a TypeError." 199 // " 2. Set input's used flag." 200 if (input->bodyUsed()) { 201 exceptionState.throwTypeError( 202 "Cannot construct a Request with a Request object that has already been used."); 203 return 0; 204 } 205 input->setBodyUsed(); 206 // "2. Let |request| be |input|'s associated request, if |input| is a 207 // Request object, and a new request otherwise." 208 // "3. Set |request| to a restricted copy of itself." 209 FetchRequestData* request(input->request()->createRestrictedCopy(context, SecurityOrigin::create(context->url()))); 210 // "4. Let |fallbackMode| be null." 211 // "5. Let |fallbackCredentials| be null." 212 // Instead of using null as a special fallback value, just pass the current 213 // mode and credentials; it has the same effect. 214 const FetchRequestData::Mode currentMode = request->mode(); 215 const FetchRequestData::Credentials currentCredentials = request->credentials(); 216 return createRequestWithRequestData(context, request, RequestInit(context, init, exceptionState), currentMode, currentCredentials, exceptionState); 217 } 218 219 Request* Request::create(ExecutionContext* context, FetchRequestData* request) 220 { 221 Request* r = new Request(context, request); 222 r->suspendIfNeeded(); 223 return r; 224 } 225 226 Request::Request(ExecutionContext* context, FetchRequestData* request) 227 : Body(context) 228 , m_request(request) 229 , m_headers(Headers::create(m_request->headerList())) 230 { 231 m_headers->setGuard(Headers::RequestGuard); 232 } 233 234 Request* Request::create(ExecutionContext* context, const WebServiceWorkerRequest& webRequest) 235 { 236 Request* r = new Request(context, webRequest); 237 r->suspendIfNeeded(); 238 return r; 239 } 240 241 Request* Request::create(const Request& copyFrom) 242 { 243 Request* r = new Request(copyFrom); 244 r->suspendIfNeeded(); 245 return r; 246 } 247 248 Request::Request(ExecutionContext* context, const WebServiceWorkerRequest& webRequest) 249 : Body(context) 250 , m_request(FetchRequestData::create(webRequest)) 251 , m_headers(Headers::create(m_request->headerList())) 252 { 253 m_headers->setGuard(Headers::RequestGuard); 254 } 255 256 Request::Request(const Request& copy_from) 257 : Body(copy_from) 258 , m_request(copy_from.m_request) 259 , m_headers(copy_from.m_headers->createCopy()) 260 { 261 } 262 263 264 String Request::method() const 265 { 266 // "The method attribute's getter must return request's method." 267 return m_request->method(); 268 } 269 270 String Request::url() const 271 { 272 // The url attribute's getter must return request's url, serialized with the exclude fragment flag set. 273 if (!m_request->url().hasFragmentIdentifier()) 274 return m_request->url(); 275 KURL url(m_request->url()); 276 url.removeFragmentIdentifier(); 277 return url; 278 } 279 280 String Request::referrer() const 281 { 282 // "The referrer attribute's getter must return the empty string if 283 // request's referrer is none, and request's referrer, serialized, 284 // otherwise." 285 return m_request->referrer().referrer().referrer; 286 } 287 288 String Request::mode() const 289 { 290 // "The mode attribute's getter must return the value corresponding to the 291 // first matching statement, switching on request's mode:" 292 switch (m_request->mode()) { 293 case FetchRequestData::SameOriginMode: 294 return "same-origin"; 295 case FetchRequestData::NoCORSMode: 296 return "no-cors"; 297 case FetchRequestData::CORSMode: 298 case FetchRequestData::CORSWithForcedPreflight: 299 return "cors"; 300 } 301 ASSERT_NOT_REACHED(); 302 return ""; 303 } 304 305 String Request::credentials() const 306 { 307 // "The credentials attribute's getter must return the value corresponding 308 // to the first matching statement, switching on request's credentials 309 // mode:" 310 switch (m_request->credentials()) { 311 case FetchRequestData::OmitCredentials: 312 return "omit"; 313 case FetchRequestData::SameOriginCredentials: 314 return "same-origin"; 315 case FetchRequestData::IncludeCredentials: 316 return "include"; 317 } 318 ASSERT_NOT_REACHED(); 319 return ""; 320 } 321 322 Request* Request::clone() const 323 { 324 return Request::create(*this); 325 } 326 327 void Request::populateWebServiceWorkerRequest(WebServiceWorkerRequest& webRequest) 328 { 329 webRequest.setMethod(method()); 330 webRequest.setURL(m_request->url()); 331 m_headers->forEach(new FillWebRequestHeaders(&webRequest)); 332 webRequest.setReferrer(m_request->referrer().referrer().referrer, static_cast<WebReferrerPolicy>(m_request->referrer().referrer().referrerPolicy)); 333 // FIXME: How can we set isReload properly? What is the correct place to load it in to the Request object? We should investigate the right way 334 // to plumb this information in to here. 335 } 336 337 void Request::setBodyBlobHandle(PassRefPtr<BlobDataHandle> blobDataHandle) 338 { 339 m_request->setBlobDataHandle(blobDataHandle); 340 } 341 342 void Request::clearHeaderList() 343 { 344 m_request->headerList()->clearList(); 345 } 346 347 PassRefPtr<BlobDataHandle> Request::blobDataHandle() 348 { 349 return m_request->blobDataHandle(); 350 } 351 352 void Request::trace(Visitor* visitor) 353 { 354 Body::trace(visitor); 355 visitor->trace(m_request); 356 visitor->trace(m_headers); 357 } 358 359 } // namespace blink 360