void DocumentThreadableLoader::loadFallbackRequestForServiceWorker()
{
    clearResource();
    ResourceRequest fallbackRequest(m_fallbackRequestForServiceWorker);
    m_fallbackRequestForServiceWorker = ResourceRequest();
    dispatchInitialRequest(fallbackRequest);
    // |this| may be dead here in async mode.
}
Example #2
0
void DocumentThreadableLoader::loadFallbackRequestForServiceWorker() {
  clearResource();
  ResourceRequest fallbackRequest(m_fallbackRequestForServiceWorker);
  m_fallbackRequestForServiceWorker = ResourceRequest();
  dispatchInitialRequest(fallbackRequest);
}
Example #3
0
void DocumentThreadableLoader::start(const ResourceRequest& request) {
  // Setting an outgoing referer is only supported in the async code path.
  DCHECK(m_async || request.httpReferrer().isEmpty());

  m_sameOriginRequest =
      getSecurityOrigin()->canRequestNoSuborigin(request.url());
  m_requestContext = request.requestContext();
  m_redirectMode = request.fetchRedirectMode();

  if (!m_sameOriginRequest &&
      m_options.crossOriginRequestPolicy == DenyCrossOriginRequests) {
    InspectorInstrumentation::
        documentThreadableLoaderFailedToStartLoadingForClient(m_document,
                                                              m_client);
    ThreadableLoaderClient* client = m_client;
    clear();
    client->didFail(ResourceError(errorDomainBlinkInternal, 0,
                                  request.url().getString(),
                                  "Cross origin requests are not supported."));
    return;
  }

  m_requestStartedSeconds = monotonicallyIncreasingTime();

  // Save any headers on the request here. If this request redirects
  // cross-origin, we cancel the old request create a new one, and copy these
  // headers.
  m_requestHeaders = request.httpHeaderFields();

  // DocumentThreadableLoader is used by all javascript initiated fetch, so we
  // use this chance to record non-GET fetch script requests. However, this is
  // based on the following assumptions, so please be careful when adding
  // similar logic:
  // - ThreadableLoader is used as backend for all javascript initiated network
  //   fetches.
  // - Note that ThreadableLoader is also used for non-network fetch such as
  //   FileReaderLoader. However it emulates GET method so signal is not
  //   recorded here.
  // - ThreadableLoader w/ non-GET request is only created from javascript
  //   initiated fetch.
  // - Some non-script initiated fetches such as WorkerScriptLoader also use
  //   ThreadableLoader, but they are guaranteed to use GET method.
  if (request.httpMethod() != HTTPNames::GET) {
    if (Page* page = m_document->page())
      page->chromeClient().didObserveNonGetFetchFromScript();
  }

  ResourceRequest newRequest(request);
  if (m_requestContext != WebURLRequest::RequestContextFetch) {
    // When the request context is not "fetch", |crossOriginRequestPolicy|
    // represents the fetch request mode, and |credentialsRequested| represents
    // the fetch credentials mode. So we set those flags here so that we can see
    // the correct request mode and credentials mode in the service worker's
    // fetch event handler.
    switch (m_options.crossOriginRequestPolicy) {
      case DenyCrossOriginRequests:
        newRequest.setFetchRequestMode(
            WebURLRequest::FetchRequestModeSameOrigin);
        break;
      case UseAccessControl:
        if (m_options.preflightPolicy == ForcePreflight) {
          newRequest.setFetchRequestMode(
              WebURLRequest::FetchRequestModeCORSWithForcedPreflight);
        } else {
          newRequest.setFetchRequestMode(WebURLRequest::FetchRequestModeCORS);
        }
        break;
      case AllowCrossOriginRequests:
        SECURITY_CHECK(IsNoCORSAllowedContext(m_requestContext,
                                              request.skipServiceWorker()));
        newRequest.setFetchRequestMode(WebURLRequest::FetchRequestModeNoCORS);
        break;
    }
    if (m_resourceLoaderOptions.allowCredentials == AllowStoredCredentials) {
      newRequest.setFetchCredentialsMode(
          WebURLRequest::FetchCredentialsModeInclude);
    } else {
      newRequest.setFetchCredentialsMode(
          WebURLRequest::FetchCredentialsModeSameOrigin);
    }
  }

  // We assume that ServiceWorker is skipped for sync requests and unsupported
  // protocol requests by content/ code.
  if (m_async &&
      request.skipServiceWorker() == WebURLRequest::SkipServiceWorker::None &&
      SchemeRegistry::shouldTreatURLSchemeAsAllowingServiceWorkers(
          request.url().protocol()) &&
      m_document->fetcher()->isControlledByServiceWorker()) {
    if (newRequest.fetchRequestMode() == WebURLRequest::FetchRequestModeCORS ||
        newRequest.fetchRequestMode() ==
            WebURLRequest::FetchRequestModeCORSWithForcedPreflight) {
      m_fallbackRequestForServiceWorker = ResourceRequest(request);
      // m_fallbackRequestForServiceWorker is used when a regular controlling
      // service worker doesn't handle a cross origin request. When this happens
      // we still want to give foreign fetch a chance to handle the request, so
      // only skip the controlling service worker for the fallback request. This
      // is currently safe because of http://crbug.com/604084 the
      // wasFallbackRequiredByServiceWorker flag is never set when foreign fetch
      // handled a request.
      m_fallbackRequestForServiceWorker.setSkipServiceWorker(
          WebURLRequest::SkipServiceWorker::Controlling);
    }
    loadRequest(newRequest, m_resourceLoaderOptions);
    return;
  }

  dispatchInitialRequest(newRequest);
}
DocumentThreadableLoader::DocumentThreadableLoader(Document& document, ThreadableLoaderClient* client, BlockingBehavior blockingBehavior, const ResourceRequest& request, const ThreadableLoaderOptions& options, const ResourceLoaderOptions& resourceLoaderOptions)
    : m_client(client)
    , m_document(&document)
    , m_options(options)
    , m_resourceLoaderOptions(resourceLoaderOptions)
    , m_forceDoNotAllowStoredCredentials(false)
    , m_securityOrigin(m_resourceLoaderOptions.securityOrigin)
    , m_sameOriginRequest(securityOrigin()->canRequestNoSuborigin(request.url()))
    , m_crossOriginNonSimpleRequest(false)
    , m_isUsingDataConsumerHandle(false)
    , m_async(blockingBehavior == LoadAsynchronously)
    , m_requestContext(request.requestContext())
    , m_timeoutTimer(this, &DocumentThreadableLoader::didTimeout)
    , m_requestStartedSeconds(0.0)
    , m_corsRedirectLimit(kMaxCORSRedirects)
    , m_redirectMode(request.fetchRedirectMode())
{
    ASSERT(client);
    // Setting an outgoing referer is only supported in the async code path.
    ASSERT(m_async || request.httpReferrer().isEmpty());

    if (!m_sameOriginRequest && m_options.crossOriginRequestPolicy == DenyCrossOriginRequests) {
        ThreadableLoaderClient* client = m_client;
        clear();
        client->didFail(ResourceError(errorDomainBlinkInternal, 0, request.url().string(), "Cross origin requests are not supported."));
        // |this| may be dead here.
        return;
    }

    m_requestStartedSeconds = monotonicallyIncreasingTime();

    // Save any CORS simple headers on the request here. If this request redirects cross-origin, we cancel the old request
    // create a new one, and copy these headers.
    const HTTPHeaderMap& headerMap = request.httpHeaderFields();
    for (const auto& header : headerMap) {
        if (FetchUtils::isSimpleHeader(header.key, header.value)) {
            m_simpleRequestHeaders.add(header.key, header.value);
        } else if (equalIgnoringCase(header.key, HTTPNames::Range) && m_options.crossOriginRequestPolicy == UseAccessControl && m_options.preflightPolicy == PreventPreflight) {
            // Allow an exception for the "range" header for when CORS callers request no preflight, this ensures cross-origin
            // redirects work correctly for crossOrigin enabled WebURLRequest::RequestContextVideo type requests.
            m_simpleRequestHeaders.add(header.key, header.value);
        }
    }

    // DocumentThreadableLoader is used by all javascript initiated fetch, so
    // we use this chance to record non-GET fetch script requests.
    // However, this is based on the following assumptions, so please be careful
    // when adding similar logic:
    // - ThreadableLoader is used as backend for all javascript initiated network
    //   fetches.
    //   - Note that ThreadableLoader is also used for non-network fetch such as
    //     FileReaderLoader. However it emulates GET method so signal is not
    //     recorded here.
    // - ThreadableLoader w/ non-GET request is only created from javascript
    //   initiated fetch.
    //   - Some non-script initiated fetches such as WorkerScriptLoader also use
    //     ThreadableLoader, but they are guaranteed to use GET method.
    if (request.httpMethod() != HTTPNames::GET) {
        if (Page* page = document.page())
            page->chromeClient().didObserveNonGetFetchFromScript();
    }

    // If the fetch request will be handled by the ServiceWorker, the
    // FetchRequestMode of the request must be FetchRequestModeCORS or
    // FetchRequestModeCORSWithForcedPreflight. Otherwise the ServiceWorker can
    // return a opaque response which is from the other origin site and the
    // script in the page can read the content.
    //
    // We assume that ServiceWorker is skipped for sync requests and unsupported
    // protocol requests by content/ code.
    if (m_async && !request.skipServiceWorker() && SchemeRegistry::shouldTreatURLSchemeAsAllowingServiceWorkers(request.url().protocol()) && document.fetcher()->isControlledByServiceWorker()) {
        ResourceRequest newRequest(request);
        // FetchRequestMode should be set by the caller. But the expected value
        // of FetchRequestMode is not speced yet except for XHR. So we set here.
        // FIXME: When we support fetch API in document, this value should not
        // be overridden here.
        if (options.preflightPolicy == ForcePreflight)
            newRequest.setFetchRequestMode(WebURLRequest::FetchRequestModeCORSWithForcedPreflight);
        else
            newRequest.setFetchRequestMode(WebURLRequest::FetchRequestModeCORS);

        m_fallbackRequestForServiceWorker = ResourceRequest(request);
        m_fallbackRequestForServiceWorker.setSkipServiceWorker(true);

        loadRequest(newRequest, m_resourceLoaderOptions);
        return;
    }

    dispatchInitialRequest(request);
    // |this| may be dead here in async mode.
}