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 "FetchManager.h" 7 8 #include "bindings/core/v8/ExceptionState.h" 9 #include "bindings/core/v8/ScriptPromiseResolver.h" 10 #include "bindings/core/v8/ScriptState.h" 11 #include "bindings/core/v8/V8ThrowException.h" 12 #include "core/dom/ExceptionCode.h" 13 #include "core/fetch/FetchUtils.h" 14 #include "core/fileapi/Blob.h" 15 #include "core/loader/ThreadableLoader.h" 16 #include "core/loader/ThreadableLoaderClient.h" 17 #include "modules/serviceworkers/FetchRequestData.h" 18 #include "modules/serviceworkers/Response.h" 19 #include "modules/serviceworkers/ResponseInit.h" 20 #include "platform/network/ResourceRequest.h" 21 #include "platform/weborigin/SecurityOrigin.h" 22 #include "public/platform/WebURLRequest.h" 23 #include "wtf/HashSet.h" 24 25 namespace blink { 26 27 class FetchManager::Loader : public ThreadableLoaderClient { 28 public: 29 Loader(ExecutionContext*, FetchManager*, PassRefPtr<ScriptPromiseResolver>, const FetchRequestData*); 30 ~Loader(); 31 virtual void didReceiveResponse(unsigned long, const ResourceResponse&); 32 virtual void didFinishLoading(unsigned long, double); 33 virtual void didFail(const ResourceError&); 34 virtual void didFailAccessControlCheck(const ResourceError&); 35 virtual void didFailRedirectCheck(); 36 virtual void didDownloadData(int); 37 38 void start(); 39 void cleanup(); 40 41 private: 42 void performBasicFetch(); 43 void performNetworkError(); 44 void performHTTPFetch(); 45 void failed(); 46 void notifyFinished(); 47 48 ExecutionContext* m_executionContext; 49 FetchManager* m_fetchManager; 50 RefPtr<ScriptPromiseResolver> m_resolver; 51 Persistent<FetchRequestData> m_request; 52 RefPtr<ThreadableLoader> m_loader; 53 ResourceResponse m_response; 54 long long m_downloadedBlobLength; 55 bool m_corsFlag; 56 bool m_corsPreflightFlag; 57 bool m_failed; 58 }; 59 60 FetchManager::Loader::Loader(ExecutionContext* executionContext, FetchManager* fetchManager, PassRefPtr<ScriptPromiseResolver> resolver, const FetchRequestData* request) 61 : m_executionContext(executionContext) 62 , m_fetchManager(fetchManager) 63 , m_resolver(resolver) 64 , m_request(request->createCopy()) 65 , m_downloadedBlobLength(0) 66 , m_corsFlag(false) 67 , m_corsPreflightFlag(false) 68 , m_failed(false) 69 { 70 } 71 72 FetchManager::Loader::~Loader() 73 { 74 if (m_loader) 75 m_loader->cancel(); 76 } 77 78 void FetchManager::Loader::didReceiveResponse(unsigned long, const ResourceResponse& response) 79 { 80 m_response = response; 81 } 82 83 void FetchManager::Loader::didFinishLoading(unsigned long, double) 84 { 85 if (!m_resolver->executionContext() || m_resolver->executionContext()->activeDOMObjectsAreStopped()) 86 return; 87 88 OwnPtr<BlobData> blobData = BlobData::create(); 89 String filePath = m_response.downloadedFilePath(); 90 if (!filePath.isEmpty() && m_downloadedBlobLength) { 91 blobData->appendFile(filePath); 92 blobData->setContentType(m_response.mimeType()); 93 } 94 FetchResponseData* response = FetchResponseData::create(); 95 response->setStatus(m_response.httpStatusCode()); 96 response->setStatusMessage(m_response.httpStatusText()); 97 HTTPHeaderMap::const_iterator end = m_response.httpHeaderFields().end(); 98 for (HTTPHeaderMap::const_iterator it = m_response.httpHeaderFields().begin(); it != end; ++it) { 99 response->headerList()->append(it->key, it->value); 100 } 101 response->setBlobDataHandle(BlobDataHandle::create(blobData.release(), m_downloadedBlobLength)); 102 response->setURL(m_request->url()); 103 104 switch (m_request->tainting()) { 105 case FetchRequestData::BasicTainting: 106 response = response->createBasicFilteredResponse(); 107 break; 108 case FetchRequestData::CORSTainting: 109 response = response->createCORSFilteredResponse(); 110 break; 111 case FetchRequestData::OpaqueTainting: 112 response = response->createOpaqueFilteredResponse(); 113 break; 114 } 115 m_resolver->resolve(Response::create(m_resolver->executionContext(), response)); 116 notifyFinished(); 117 } 118 119 void FetchManager::Loader::didFail(const ResourceError& error) 120 { 121 failed(); 122 } 123 124 void FetchManager::Loader::didFailAccessControlCheck(const ResourceError& error) 125 { 126 failed(); 127 } 128 129 void FetchManager::Loader::didFailRedirectCheck() 130 { 131 failed(); 132 } 133 134 void FetchManager::Loader::didDownloadData(int dataLength) 135 { 136 m_downloadedBlobLength += dataLength; 137 } 138 139 void FetchManager::Loader::start() 140 { 141 // "1. If |request|'s url contains a Known HSTS Host, modify it per the 142 // requirements of the 'URI [sic] Loading and Port Mapping' chapter of HTTP 143 // Strict Transport Security." 144 // FIXME: Implement this. 145 146 // "2. If |request|'s referrer is not none, set |request|'s referrer to the 147 // result of invoking determine |request|'s referrer." 148 // We set the referrer using workerGlobalScope's URL in 149 // WorkerThreadableLoader. 150 151 // "3. If |request|'s synchronous flag is unset and fetch is not invoked 152 // recursively, run the remaining steps asynchronously." 153 // We don't support synchronous flag. 154 155 // "4. Let response be the value corresponding to the first matching 156 // statement:" 157 158 // "- should fetching |request| be blocked as mixed content returns blocked 159 // - should fetching |request| be blocked as content security returns 160 // blocked 161 // A network error." 162 // We do mixed content checking and CSP checking in ResourceFetcher. 163 164 // "- |request|'s url's origin is |request|'s origin and the |CORS flag| is 165 // unset" 166 // "- |request|'s url's scheme is 'data' and |request|'s same-origin data 167 // URL flag is set" 168 // "- |request|'s url's scheme is 'about'" 169 if ((SecurityOrigin::create(m_request->url())->isSameSchemeHostPort(m_request->origin().get()) && !m_corsFlag) 170 || (m_request->url().protocolIsData() && m_request->sameOriginDataURLFlag()) 171 || (m_request->url().protocolIsAbout())) { 172 // "The result of performing a basic fetch using request." 173 performBasicFetch(); 174 return; 175 } 176 177 // "- |request|'s mode is |same-origin|" 178 if (m_request->mode() == FetchRequestData::SameOriginMode) { 179 // "A network error." 180 performNetworkError(); 181 return; 182 } 183 184 // "- |request|'s mode is |no CORS|" 185 if (m_request->mode() == FetchRequestData::NoCORSMode) { 186 // "Set |request|'s response tainting to |opaque|." 187 m_request->setResponseTainting(FetchRequestData::OpaqueTainting); 188 // "The result of performing a basic fetch using |request|." 189 performBasicFetch(); 190 return; 191 } 192 193 // "- |request|'s url's scheme is not one of 'http' and 'https'" 194 if (!m_request->url().protocolIsInHTTPFamily()) { 195 // "A network error." 196 performNetworkError(); 197 return; 198 } 199 200 // "- |request|'s mode is |CORS-with-forced-preflight|. 201 // "- |request|'s unsafe request flag is set and either |request|'s method 202 // is not a simple method or a header in |request|'s header list is not a 203 // simple header" 204 if (m_request->mode() == FetchRequestData::CORSWithForcedPreflight 205 || (m_request->unsafeRequestFlag() 206 && (!FetchUtils::isSimpleMethod(m_request->method()) 207 || m_request->headerList()->containsNonSimpleHeader()))) { 208 // "Set |request|'s response tainting to |CORS|." 209 m_request->setResponseTainting(FetchRequestData::CORSTainting); 210 // "The result of performing an HTTP fetch using |request| with the 211 // |CORS flag| and |CORS preflight flag| set." 212 m_corsFlag = true; 213 m_corsPreflightFlag = true; 214 performHTTPFetch(); 215 return; 216 } 217 218 // "- Otherwise 219 // Set |request|'s response tainting to |CORS|." 220 m_request->setResponseTainting(FetchRequestData::CORSTainting); 221 // "The result of performing an HTTP fetch using |request| with the 222 // |CORS flag| set." 223 m_corsFlag = true; 224 m_corsPreflightFlag = false; 225 performHTTPFetch(); 226 } 227 228 void FetchManager::Loader::cleanup() 229 { 230 // Prevent notification 231 m_fetchManager = 0; 232 233 if (m_loader) { 234 m_loader->cancel(); 235 m_loader.clear(); 236 } 237 } 238 239 void FetchManager::Loader::performBasicFetch() 240 { 241 // "To perform a basic fetch using |request|, switch on |request|'s url's 242 // scheme, and run the associated steps:" 243 if (m_request->url().protocolIsInHTTPFamily()) { 244 // "Return the result of performing an HTTP fetch using |request|." 245 m_corsFlag = false; 246 m_corsPreflightFlag = false; 247 performHTTPFetch(); 248 } else { 249 // FIXME: implement other protocols. 250 performNetworkError(); 251 } 252 } 253 254 void FetchManager::Loader::performNetworkError() 255 { 256 failed(); 257 } 258 259 void FetchManager::Loader::performHTTPFetch() 260 { 261 ASSERT(m_request->url().protocolIsInHTTPFamily()); 262 // CORS preflight fetch procedure is implemented inside DocumentThreadableLoader. 263 264 // "1. Let |HTTPRequest| be a copy of |request|, except that |HTTPRequest|'s 265 // body is a tee of |request|'s body." 266 // We use ResourceRequest class for HTTPRequest. 267 // FIXME: Support body. 268 ResourceRequest request(m_request->url()); 269 request.setRequestContext(WebURLRequest::RequestContextFetch); 270 request.setDownloadToFile(true); 271 request.setHTTPMethod(m_request->method()); 272 const Vector<OwnPtr<FetchHeaderList::Header> >& list = m_request->headerList()->list(); 273 for (size_t i = 0; i < list.size(); ++i) { 274 request.addHTTPHeaderField(AtomicString(list[i]->first), AtomicString(list[i]->second)); 275 } 276 277 if (m_request->method() != "GET" && m_request->method() != "HEAD") { 278 RefPtr<BlobDataHandle> blobDataHandle = m_request->blobDataHandle(); 279 if (blobDataHandle.get()) { 280 RefPtr<FormData> httpBody(FormData::create()); 281 httpBody->appendBlob(blobDataHandle->uuid(), blobDataHandle); 282 request.setHTTPBody(httpBody); 283 } 284 } 285 286 // "2. Append `Referer`/empty byte sequence, if |HTTPRequest|'s |referrer| 287 // is none, and `Referer`/|HTTPRequest|'s referrer, serialized and utf-8 288 // encoded, otherwise, to HTTPRequest's header list. 289 // We set the referrer using workerGlobalScope's URL in 290 // WorkerThreadableLoader. 291 292 // "3. Append `Host`, ..." 293 // FIXME: Implement this when the spec is fixed. 294 295 // "4.If |HTTPRequest|'s force Origin header flag is set, append `Origin`/ 296 // |HTTPRequest|'s origin, serialized and utf-8 encoded, to |HTTPRequest|'s 297 // header list." 298 // We set Origin header in updateRequestForAccessControl() called from 299 // DocumentThreadableLoader::makeCrossOriginAccessRequest 300 301 // "5. Let |credentials flag| be set if either |HTTPRequest|'s credentials 302 // mode is |include|, or |HTTPRequest|'s credentials mode is |same-origin| 303 // and the |CORS flag| is unset, and unset otherwise. 304 ResourceLoaderOptions resourceLoaderOptions; 305 resourceLoaderOptions.dataBufferingPolicy = DoNotBufferData; 306 if (m_request->credentials() == FetchRequestData::IncludeCredentials 307 || (m_request->credentials() == FetchRequestData::SameOriginCredentials && !m_corsFlag)) { 308 resourceLoaderOptions.allowCredentials = AllowStoredCredentials; 309 } 310 311 ThreadableLoaderOptions threadableLoaderOptions; 312 if (m_corsPreflightFlag) 313 threadableLoaderOptions.preflightPolicy = ForcePreflight; 314 if (m_corsFlag) 315 threadableLoaderOptions.crossOriginRequestPolicy = UseAccessControl; 316 else 317 threadableLoaderOptions.crossOriginRequestPolicy = AllowCrossOriginRequests; 318 319 320 m_loader = ThreadableLoader::create(*m_executionContext, this, request, threadableLoaderOptions, resourceLoaderOptions); 321 } 322 323 void FetchManager::Loader::failed() 324 { 325 if (m_failed) 326 return; 327 if (!m_resolver->executionContext() || m_resolver->executionContext()->activeDOMObjectsAreStopped()) 328 return; 329 m_failed = true; 330 ScriptState* state = m_resolver->scriptState(); 331 ScriptState::Scope scope(state); 332 m_resolver->reject(V8ThrowException::createTypeError("Failed to fetch", state->isolate())); 333 notifyFinished(); 334 } 335 336 void FetchManager::Loader::notifyFinished() 337 { 338 if (m_fetchManager) 339 m_fetchManager->onLoaderFinished(this); 340 } 341 342 FetchManager::FetchManager(ExecutionContext* executionContext) 343 : m_executionContext(executionContext) 344 { 345 } 346 347 FetchManager::~FetchManager() 348 { 349 for (HashSet<OwnPtr<Loader> >::iterator it = m_loaders.begin(); it != m_loaders.end(); ++it) { 350 (*it)->cleanup(); 351 } 352 } 353 354 ScriptPromise FetchManager::fetch(ScriptState* scriptState, const FetchRequestData* request) 355 { 356 RefPtr<ScriptPromiseResolver> resolver = ScriptPromiseResolver::create(scriptState); 357 ScriptPromise promise = resolver->promise(); 358 359 OwnPtr<Loader> ownLoader(adoptPtr(new Loader(m_executionContext, this, resolver.release(), request))); 360 Loader* loader = m_loaders.add(ownLoader.release()).storedValue->get(); 361 loader->start(); 362 return promise; 363 } 364 365 void FetchManager::onLoaderFinished(Loader* loader) 366 { 367 m_loaders.remove(loader); 368 } 369 370 } // namespace blink 371