void NetworkResourceLoader::willSendRedirectedRequest(ResourceRequest&& request, WebCore::ResourceRequest&& redirectRequest, ResourceResponse&& redirectResponse) { ++m_redirectCount; if (isSynchronous()) { ResourceRequest overridenRequest = redirectRequest; // FIXME: This needs to be fixed to follow the redirect correctly even for cross-domain requests. // This includes at least updating host records, and comparing the current request instead of the original request here. if (!protocolHostAndPortAreEqual(originalRequest().url(), redirectRequest.url())) { ASSERT(m_synchronousLoadData->error.isNull()); m_synchronousLoadData->error = SynchronousLoaderClient::platformBadResponseError(); m_networkLoad->clearCurrentRequest(); overridenRequest = ResourceRequest(); } continueWillSendRequest(WTFMove(overridenRequest)); return; } send(Messages::WebResourceLoader::WillSendRequest(redirectRequest, redirectResponse)); #if ENABLE(NETWORK_CACHE) if (canUseCachedRedirect(request)) NetworkCache::singleton().storeRedirect(request, redirectResponse, redirectRequest); #else UNUSED_PARAM(request); #endif }
bool ApplicationCacheHost::maybeLoadFallbackForRedirect(ResourceLoader* resourceLoader, ResourceRequest& request, const ResourceResponse& redirectResponse) { if (!redirectResponse.isNull() && !protocolHostAndPortAreEqual(request.url(), redirectResponse.url())) if (scheduleLoadFallbackResourceFromApplicationCache(resourceLoader)) return true; return false; }
void ResourceHandle::willSendRequest(ResourceRequest& request, const ResourceResponse& redirectResponse) { const KURL& url = request.url(); d->m_user = url.user(); d->m_pass = url.pass(); d->m_lastHTTPMethod = request.httpMethod(); request.removeCredentials(); if (!protocolHostAndPortAreEqual(request.url(), redirectResponse.url())) { // If the network layer carries over authentication headers from the original request // in a cross-origin redirect, we want to clear those headers here. request.clearHTTPAuthorization(); } else { // Only consider applying authentication credentials if this is actually a redirect and the redirect // URL didn't include credentials of its own. if (d->m_user.isEmpty() && d->m_pass.isEmpty() && !redirectResponse.isNull()) { Credential credential = CredentialStorage::get(request.url()); if (!credential.isEmpty()) { d->m_initialCredential = credential; // FIXME: Support Digest authentication, and Proxy-Authorization. applyBasicAuthorizationHeader(request, d->m_initialCredential); } } } RefPtr<ResourceHandle> protect(this); client()->willSendRequest(this, request, redirectResponse); // Client call may not preserve the session, especially if the request is sent over IPC. if (!request.isNull()) request.setStorageSession(d->m_storageSession.get()); }
bool ApplicationCache::isURLInOnlineWhitelist(const URL& url) { for (auto& whitelistURL : m_onlineWhitelist) { if (protocolHostAndPortAreEqual(url, whitelistURL) && url.string().startsWith(whitelistURL.string())) return true; } return false; }
bool ApplicationCache::isURLInOnlineWhitelist(const KURL& url) { size_t whitelistSize = m_onlineWhitelist.size(); for (size_t i = 0; i < whitelistSize; ++i) { if (protocolHostAndPortAreEqual(url, m_onlineWhitelist[i]) && url.string().startsWith(m_onlineWhitelist[i].string())) return true; } return false; }
void WebCoreSynchronousLoader::willSendRequest(ResourceHandle* handle, ResourceRequest& request, const ResourceResponse& /*redirectResponse*/) { // FIXME: This needs to be fixed to follow the redirect correctly even for cross-domain requests. if (!protocolHostAndPortAreEqual(handle->firstRequest().url(), request.url())) { ASSERT(m_error.isNull()); m_error.setIsCancellation(true); request = ResourceRequest(); return; } }
void ResourceLoader::willSendRequest(ResourceHandle*, ResourceRequest& request, const ResourceResponse& redirectResponse) { #if ENABLE(OFFLINE_WEB_APPLICATIONS) if (!redirectResponse.isNull() && !protocolHostAndPortAreEqual(request.url(), redirectResponse.url())) { if (scheduleLoadFallbackResourceFromApplicationCache()) return; } #endif willSendRequest(request, redirectResponse); }
bool ApplicationCache::urlMatchesFallbackNamespace(const URL& url, URL* fallbackURL) { for (auto& fallback : m_fallbackURLs) { if (protocolHostAndPortAreEqual(url, fallback.first) && url.string().startsWith(fallback.first.string())) { if (fallbackURL) *fallbackURL = fallback.second; return true; } } return false; }
bool ApplicationCache::urlMatchesFallbackNamespace(const KURL& url, KURL* fallbackURL) { size_t fallbackCount = m_fallbackURLs.size(); for (size_t i = 0; i < fallbackCount; ++i) { if (protocolHostAndPortAreEqual(url, m_fallbackURLs[i].first) && url.string().startsWith(m_fallbackURLs[i].first.string())) { if (fallbackURL) *fallbackURL = m_fallbackURLs[i].second; return true; } } return false; }
void SynchronousNetworkLoaderClient::willSendRequest(NetworkResourceLoader* loader, ResourceRequest& proposedRequest, const ResourceResponse& /* redirectResponse */) { // FIXME: This needs to be fixed to follow the redirect correctly even for cross-domain requests. // This includes at least updating host records, and comparing the current request instead of the original request here. if (!protocolHostAndPortAreEqual(m_originalRequest.url(), proposedRequest.url())) { ASSERT(m_error.isNull()); m_error = SynchronousLoaderClient::platformBadResponseError(); proposedRequest = ResourceRequest(); } m_currentRequest = proposedRequest; loader->continueWillSendRequest(m_currentRequest); }
void WebCoreSynchronousLoaderClient::willSendRequest(ResourceHandle* handle, ResourceRequest& request, const ResourceResponse& /*redirectResponse*/) { // FIXME: This needs to be fixed to follow the redirect correctly even for cross-domain requests. if (!protocolHostAndPortAreEqual(handle->firstRequest().url(), request.url())) { ASSERT(!m_error.cfError()); RetainPtr<CFErrorRef> cfError(AdoptCF, CFErrorCreate(kCFAllocatorDefault, kCFErrorDomainCFNetwork, kCFURLErrorBadServerResponse, 0)); m_error = cfError.get(); m_isDone = true; CFURLRequestRef nullRequest = 0; request = nullRequest; return; } }
void ApplicationCacheHost::maybeLoadFallbackSynchronously(const ResourceRequest& request, ResourceError& error, ResourceResponse& response, Vector<char>& data) { // If normal loading results in a redirect to a resource with another origin (indicative of a captive portal), or a 4xx or 5xx status code or equivalent, // or if there were network errors (but not if the user canceled the download), then instead get, from the cache, the resource of the fallback entry // corresponding to the matched namespace. if ((!error.isNull() && !error.isCancellation()) || response.httpStatusCode() / 100 == 4 || response.httpStatusCode() / 100 == 5 || !protocolHostAndPortAreEqual(request.url(), response.url())) { ApplicationCacheResource* resource; if (getApplicationCacheFallbackResource(request, resource)) { response = resource->response(); data.clear(); data.append(resource->data()->data(), resource->data()->size()); } } }
void ApplicationCacheHost::maybeLoadFallbackSynchronously(const ResourceRequest& request, ResourceError& error, ResourceResponse& response, RefPtr<SharedBuffer>& data) { // If normal loading results in a redirect to a resource with another origin (indicative of a captive portal), or a 4xx or 5xx status code or equivalent, // or if there were network errors (but not if the user canceled the download), then instead get, from the cache, the resource of the fallback entry // corresponding to the matched namespace. if ((!error.isNull() && !error.isCancellation()) || response.httpStatusCode() / 100 == 4 || response.httpStatusCode() / 100 == 5 || !protocolHostAndPortAreEqual(request.url(), response.url())) { ApplicationCacheResource* resource; if (getApplicationCacheFallbackResource(request, resource)) { response = resource->response(); // FIXME: Clients proably do not need a copy of the SharedBuffer. // Remove the call to copy() once we ensure SharedBuffer will not be modified. data = resource->data().copy(); } } }
void ResourceHandle::willSendRequest(ResourceRequest& request, const ResourceResponse& redirectResponse) { const URL& url = request.url(); d->m_user = url.user(); d->m_pass = url.pass(); d->m_lastHTTPMethod = request.httpMethod(); request.removeCredentials(); if (!protocolHostAndPortAreEqual(request.url(), redirectResponse.url())) { // The network layer might carry over some headers from the original request that // we want to strip here because the redirect is cross-origin. request.clearHTTPAuthorization(); request.clearHTTPOrigin(); } else { // Only consider applying authentication credentials if this is actually a redirect and the redirect // URL didn't include credentials of its own. if (d->m_user.isEmpty() && d->m_pass.isEmpty() && !redirectResponse.isNull()) { Credential credential = d->m_context->storageSession().credentialStorage().get(request.url()); if (!credential.isEmpty()) { d->m_initialCredential = credential; // FIXME: Support Digest authentication, and Proxy-Authorization. applyBasicAuthorizationHeader(request, d->m_initialCredential); } } } Ref<ResourceHandle> protect(*this); if (d->m_usesAsyncCallbacks) client()->willSendRequestAsync(this, request, redirectResponse); else { client()->willSendRequest(this, request, redirectResponse); // Client call may not preserve the session, especially if the request is sent over IPC. if (!request.isNull()) { request.setStorageSession(d->m_storageSession.get()); d->m_currentRequest = request; } } }
void ApplicationCache::deleteCacheForOrigin(SecurityOrigin* origin) { Vector<KURL> urls; if (!cacheStorage().manifestURLs(&urls)) { LOG_ERROR("Failed to retrieve ApplicationCache manifest URLs"); return; } KURL originURL(KURL(), origin->toString()); size_t count = urls.size(); for (size_t i = 0; i < count; ++i) { if (protocolHostAndPortAreEqual(urls[i], originURL)) { ApplicationCacheGroup* group = cacheStorage().findInMemoryCacheGroup(urls[i]); if (group) group->makeObsolete(); else cacheStorage().deleteCacheGroup(urls[i]); } } }
void NetworkResourceLoader::willSendRequestAsync(ResourceHandle* handle, const ResourceRequest& request, const ResourceResponse& redirectResponse) { ASSERT_UNUSED(handle, handle == m_handle); // We only expect to get the willSendRequest callback from ResourceHandle as the result of a redirect. ASSERT(!redirectResponse.isNull()); ASSERT(RunLoop::isMain()); m_currentRequest = request; if (isSynchronous()) { // FIXME: This needs to be fixed to follow the redirect correctly even for cross-domain requests. // This includes at least updating host records, and comparing the current request instead of the original request here. if (!protocolHostAndPortAreEqual(originalRequest().url(), request.url())) { ASSERT(m_synchronousLoadData->error.isNull()); m_synchronousLoadData->error = SynchronousLoaderClient::platformBadResponseError(); m_currentRequest = ResourceRequest(); } continueWillSendRequest(m_currentRequest); return; } sendAbortingOnFailure(Messages::WebResourceLoader::WillSendRequest(request, redirectResponse)); }
void ApplicationCacheGroup::selectCache(Frame* frame, const KURL& manifestURL) { ASSERT(frame && frame->page()); if (!frame->settings()->offlineWebApplicationCacheEnabled()) return; DocumentLoader* documentLoader = frame->loader()->documentLoader(); ASSERT(!documentLoader->applicationCache()); if (manifestURL.isNull()) { selectCacheWithoutManifestURL(frame); return; } ApplicationCache* mainResourceCache = documentLoader->mainResourceApplicationCache(); if (mainResourceCache) { if (manifestURL == mainResourceCache->group()->m_manifestURL) { mainResourceCache->group()->associateDocumentLoaderWithCache(documentLoader, mainResourceCache); mainResourceCache->group()->update(frame); } else { // The main resource was loaded from cache, so the cache must have an entry for it. Mark it as foreign. ApplicationCacheResource* resource = mainResourceCache->resourceForURL(documentLoader->url()); bool inStorage = resource->storageID(); resource->addType(ApplicationCacheResource::Foreign); if (inStorage) cacheStorage().storeUpdatedType(resource, mainResourceCache); // Restart the current navigation from the top of the navigation algorithm, undoing any changes that were made // as part of the initial load. // The navigation will not result in the same resource being loaded, because "foreign" entries are never picked during navigation. frame->loader()->scheduleLocationChange(documentLoader->url(), frame->loader()->referrer(), true); } return; } // The resource was loaded from the network, check if it is a HTTP/HTTPS GET. const ResourceRequest& request = frame->loader()->activeDocumentLoader()->request(); if (!ApplicationCache::requestIsHTTPOrHTTPSGet(request)) { selectCacheWithoutManifestURL(frame); return; } // Check that the resource URL has the same scheme/host/port as the manifest URL. if (!protocolHostAndPortAreEqual(manifestURL, request.url())) { selectCacheWithoutManifestURL(frame); return; } ApplicationCacheGroup* group = cacheStorage().findOrCreateCacheGroup(manifestURL); if (ApplicationCache* cache = group->newestCache()) { ASSERT(cache->manifestResource()); group->associateDocumentLoaderWithCache(frame->loader()->documentLoader(), cache); if (!frame->loader()->documentLoader()->isLoadingMainResource()) group->finishedLoadingMainResource(frame->loader()->documentLoader()); group->update(frame); } else { bool isUpdating = group->m_cacheBeingUpdated; if (!isUpdating) group->m_cacheBeingUpdated = ApplicationCache::create(); documentLoader->setCandidateApplicationCacheGroup(group); group->m_cacheCandidates.add(documentLoader); const KURL& url = frame->loader()->documentLoader()->originalURL(); unsigned type = 0; // If the resource has already been downloaded, remove it so that it will be replaced with the implicit resource if (isUpdating) type = group->m_cacheBeingUpdated->removeResource(url); // Add the main resource URL as an implicit entry. group->addEntry(url, type | ApplicationCacheResource::Implicit); if (!frame->loader()->documentLoader()->isLoadingMainResource()) group->finishedLoadingMainResource(frame->loader()->documentLoader()); if (!isUpdating) group->update(frame); } }
void ApplicationCacheGroup::selectCache(Frame* frame, const URL& passedManifestURL) { ASSERT(frame && frame->page()); if (!frame->settings().offlineWebApplicationCacheEnabled()) return; DocumentLoader* documentLoader = frame->loader().documentLoader(); ASSERT(!documentLoader->applicationCacheHost()->applicationCache()); if (passedManifestURL.isNull()) { selectCacheWithoutManifestURL(frame); return; } // Don't access anything on disk if private browsing is enabled. if (frame->page()->usesEphemeralSession() || !frame->document()->securityOrigin()->canAccessApplicationCache(frame->tree().top().document()->securityOrigin())) { postListenerTask(ApplicationCacheHost::CHECKING_EVENT, documentLoader); postListenerTask(ApplicationCacheHost::ERROR_EVENT, documentLoader); return; } URL manifestURL(passedManifestURL); if (manifestURL.hasFragmentIdentifier()) manifestURL.removeFragmentIdentifier(); ApplicationCache* mainResourceCache = documentLoader->applicationCacheHost()->mainResourceApplicationCache(); if (mainResourceCache) { if (manifestURL == mainResourceCache->group()->m_manifestURL) { // The cache may have gotten obsoleted after we've loaded from it, but before we parsed the document and saw cache manifest. if (mainResourceCache->group()->isObsolete()) return; mainResourceCache->group()->associateDocumentLoaderWithCache(documentLoader, mainResourceCache); mainResourceCache->group()->update(frame, ApplicationCacheUpdateWithBrowsingContext); } else { // The main resource was loaded from cache, so the cache must have an entry for it. Mark it as foreign. URL resourceURL(documentLoader->responseURL()); if (resourceURL.hasFragmentIdentifier()) resourceURL.removeFragmentIdentifier(); ApplicationCacheResource* resource = mainResourceCache->resourceForURL(resourceURL); bool inStorage = resource->storageID(); resource->addType(ApplicationCacheResource::Foreign); if (inStorage) frame->page()->applicationCacheStorage().storeUpdatedType(resource, mainResourceCache); // Restart the current navigation from the top of the navigation algorithm, undoing any changes that were made // as part of the initial load. // The navigation will not result in the same resource being loaded, because "foreign" entries are never picked during navigation. frame->navigationScheduler().scheduleLocationChange(frame->document(), frame->document()->securityOrigin(), documentLoader->url(), frame->loader().referrer()); } return; } // The resource was loaded from the network, check if it is a HTTP/HTTPS GET. const ResourceRequest& request = frame->loader().activeDocumentLoader()->request(); if (!ApplicationCache::requestIsHTTPOrHTTPSGet(request)) return; // Check that the resource URL has the same scheme/host/port as the manifest URL. if (!protocolHostAndPortAreEqual(manifestURL, request.url())) return; ApplicationCacheGroup* group = frame->page()->applicationCacheStorage().findOrCreateCacheGroup(manifestURL); documentLoader->applicationCacheHost()->setCandidateApplicationCacheGroup(group); group->m_pendingMasterResourceLoaders.add(documentLoader); group->m_downloadingPendingMasterResourceLoadersCount++; ASSERT(!group->m_cacheBeingUpdated || group->m_updateStatus != Idle); group->update(frame, ApplicationCacheUpdateWithBrowsingContext); }
bool parseManifest(const KURL& manifestURL, const char* data, int length, Manifest& manifest) { ASSERT(manifest.explicitURLs.isEmpty()); ASSERT(manifest.onlineWhitelistedURLs.isEmpty()); ASSERT(manifest.fallbackURLs.isEmpty()); manifest.allowAllNetworkRequests = false; Mode mode = Explicit; RefPtr<TextResourceDecoder> decoder = TextResourceDecoder::create("text/cache-manifest", "UTF-8"); String s = decoder->decode(data, length); s += decoder->flush(); // Look for the magic signature: "^\xFEFF?CACHE MANIFEST[ \t]?" (the BOM is removed by TextResourceDecoder). // Example: "CACHE MANIFEST #comment" is a valid signature. // Example: "CACHE MANIFEST;V2" is not. if (!s.startsWith("CACHE MANIFEST")) return false; const UChar* end = s.characters() + s.length(); const UChar* p = s.characters() + 14; // "CACHE MANIFEST" is 14 characters. if (p < end && *p != ' ' && *p != '\t' && *p != '\n' && *p != '\r') return false; // Skip to the end of the line. while (p < end && *p != '\r' && *p != '\n') p++; while (1) { // Skip whitespace while (p < end && (*p == '\n' || *p == '\r' || *p == ' ' || *p == '\t')) p++; if (p == end) break; const UChar* lineStart = p; // Find the end of the line while (p < end && *p != '\r' && *p != '\n') p++; // Check if we have a comment if (*lineStart == '#') continue; // Get rid of trailing whitespace const UChar* tmp = p - 1; while (tmp > lineStart && (*tmp == ' ' || *tmp == '\t')) tmp--; String line(lineStart, tmp - lineStart + 1); if (line == "CACHE:") mode = Explicit; else if (line == "FALLBACK:") mode = Fallback; else if (line == "NETWORK:") mode = OnlineWhitelist; else if (line.endsWith(":")) mode = Unknown; else if (mode == Unknown) continue; else if (mode == Explicit || mode == OnlineWhitelist) { const UChar* p = line.characters(); const UChar* lineEnd = p + line.length(); // Look for whitespace separating the URL from subsequent ignored tokens. while (p < lineEnd && *p != '\t' && *p != ' ') p++; if (mode == OnlineWhitelist && p - line.characters() == 1 && *line.characters() == '*') { // Wildcard was found. manifest.allowAllNetworkRequests = true; continue; } KURL url(manifestURL, String(line.characters(), p - line.characters())); if (!url.isValid()) continue; if (url.hasFragmentIdentifier()) url.removeFragmentIdentifier(); if (!equalIgnoringCase(url.protocol(), manifestURL.protocol())) continue; if (mode == Explicit && manifestURL.protocolIs("https") && !protocolHostAndPortAreEqual(manifestURL, url)) continue; if (mode == Explicit) manifest.explicitURLs.add(url.string()); else manifest.onlineWhitelistedURLs.append(url); } else if (mode == Fallback) { const UChar* p = line.characters(); const UChar* lineEnd = p + line.length(); // Look for whitespace separating the two URLs while (p < lineEnd && *p != '\t' && *p != ' ') p++; if (p == lineEnd) { // There was no whitespace separating the URLs. continue; } KURL namespaceURL(manifestURL, String(line.characters(), p - line.characters())); if (!namespaceURL.isValid()) continue; if (namespaceURL.hasFragmentIdentifier()) namespaceURL.removeFragmentIdentifier(); if (!protocolHostAndPortAreEqual(manifestURL, namespaceURL)) continue; // Skip whitespace separating fallback namespace from URL. while (p < lineEnd && (*p == '\t' || *p == ' ')) p++; // Look for whitespace separating the URL from subsequent ignored tokens. const UChar* fallbackStart = p; while (p < lineEnd && *p != '\t' && *p != ' ') p++; KURL fallbackURL(manifestURL, String(fallbackStart, p - fallbackStart)); if (!fallbackURL.isValid()) continue; if (fallbackURL.hasFragmentIdentifier()) fallbackURL.removeFragmentIdentifier(); if (!protocolHostAndPortAreEqual(manifestURL, fallbackURL)) continue; manifest.fallbackURLs.append(make_pair(namespaceURL, fallbackURL)); } else ASSERT_NOT_REACHED(); } return true; }
ApplicationCacheGroup* ApplicationCacheStorage::cacheGroupForURL(const KURL& url) { loadManifestHostHashes(); // Hash the host name and see if there's a manifest with the same host. if (!m_cacheHostSet.contains(urlHostHash(url))) return 0; // Check if a cache already exists in memory. CacheGroupMap::const_iterator end = m_cachesInMemory.end(); for (CacheGroupMap::const_iterator it = m_cachesInMemory.begin(); it != end; ++it) { ApplicationCacheGroup* group = it->second; if (!protocolHostAndPortAreEqual(url, group->manifestURL())) continue; if (ApplicationCache* cache = group->newestCache()) { if (cache->resourceForURL(url)) return group; } } if (!m_database.isOpen()) return 0; // Check the database. Look for all cache groups with a newest cache. SQLiteStatement statement(m_database, "SELECT id, manifestURL, newestCache FROM CacheGroups WHERE newestCache IS NOT NULL"); if (statement.prepare() != SQLResultOk) return 0; int result; while ((result = statement.step()) == SQLResultRow) { KURL manifestURL = KURL(statement.getColumnText(1)); if (!protocolHostAndPortAreEqual(url, manifestURL)) continue; // We found a cache group that matches. Now check if the newest cache has a resource with // a matching URL. unsigned newestCacheID = (unsigned)statement.getColumnInt64(2); RefPtr<ApplicationCache> cache = loadCache(newestCacheID); if (!cache->resourceForURL(url)) continue; ApplicationCacheGroup* group = new ApplicationCacheGroup(manifestURL); group->setStorageID((unsigned)statement.getColumnInt64(0)); group->setNewestCache(cache.release()); ASSERT(!m_cachesInMemory.contains(manifestURL)); m_cachesInMemory.set(group->manifestURL(), group); return group; } if (result != SQLResultDone) LOG_ERROR("Could not load cache group, error \"%s\"", m_database.lastErrorMsg()); return 0; }