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 23namespace blink { 24 25namespace { 26 27class FillWebRequestHeaders : public HeadersForEachCallback { 28public: 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 43private: 44 WebServiceWorkerRequest* m_webRequest; 45}; 46 47} // namespace 48 49Request* 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 162Request* Request::create(ExecutionContext* context, const String& input, ExceptionState& exceptionState) 163{ 164 return create(context, input, Dictionary(), exceptionState); 165} 166 167Request* 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 190Request* Request::create(ExecutionContext* context, Request* input, ExceptionState& exceptionState) 191{ 192 return create(context, input, Dictionary(), exceptionState); 193} 194 195Request* 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 219Request* Request::create(ExecutionContext* context, FetchRequestData* request) 220{ 221 Request* r = new Request(context, request); 222 r->suspendIfNeeded(); 223 return r; 224} 225 226Request::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 234Request* Request::create(ExecutionContext* context, const WebServiceWorkerRequest& webRequest) 235{ 236 Request* r = new Request(context, webRequest); 237 r->suspendIfNeeded(); 238 return r; 239} 240 241Request* Request::create(const Request& copyFrom) 242{ 243 Request* r = new Request(copyFrom); 244 r->suspendIfNeeded(); 245 return r; 246} 247 248Request::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 256Request::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 264String Request::method() const 265{ 266 // "The method attribute's getter must return request's method." 267 return m_request->method(); 268} 269 270String 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 280String 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 288String 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 305String 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 322Request* Request::clone() const 323{ 324 return Request::create(*this); 325} 326 327void 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 337void Request::setBodyBlobHandle(PassRefPtr<BlobDataHandle> blobDataHandle) 338{ 339 m_request->setBlobDataHandle(blobDataHandle); 340} 341 342void Request::clearHeaderList() 343{ 344 m_request->headerList()->clearList(); 345} 346 347PassRefPtr<BlobDataHandle> Request::blobDataHandle() 348{ 349 return m_request->blobDataHandle(); 350} 351 352void 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