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