ServiceWorkerClientFetch.cpp [plain text]
#include "config.h"
#include "ServiceWorkerClientFetch.h"
#if ENABLE(SERVICE_WORKER)
#include "DataReference.h"
#include "WebSWClientConnection.h"
#include "WebServiceWorkerProvider.h"
#include <WebCore/CrossOriginAccessControl.h>
#include <WebCore/MIMETypeRegistry.h>
#include <WebCore/NotImplemented.h>
#include <WebCore/ResourceError.h>
using namespace WebCore;
namespace WebKit {
Ref<ServiceWorkerClientFetch> ServiceWorkerClientFetch::create(WebServiceWorkerProvider& serviceWorkerProvider, Ref<WebCore::ResourceLoader>&& loader, uint64_t identifier, Ref<WebSWClientConnection>&& connection, bool shouldClearReferrerOnHTTPSToHTTPRedirect, Callback&& callback)
{
auto fetch = adoptRef(*new ServiceWorkerClientFetch { serviceWorkerProvider, WTFMove(loader), identifier, WTFMove(connection), shouldClearReferrerOnHTTPSToHTTPRedirect, WTFMove(callback) });
fetch->start();
return fetch;
}
ServiceWorkerClientFetch::~ServiceWorkerClientFetch()
{
}
ServiceWorkerClientFetch::ServiceWorkerClientFetch(WebServiceWorkerProvider& serviceWorkerProvider, Ref<WebCore::ResourceLoader>&& loader, uint64_t identifier, Ref<WebSWClientConnection>&& connection, bool shouldClearReferrerOnHTTPSToHTTPRedirect, Callback&& callback)
: m_serviceWorkerProvider(serviceWorkerProvider)
, m_loader(WTFMove(loader))
, m_identifier(identifier)
, m_connection(WTFMove(connection))
, m_callback(WTFMove(callback))
, m_shouldClearReferrerOnHTTPSToHTTPRedirect(shouldClearReferrerOnHTTPSToHTTPRedirect)
{
}
void ServiceWorkerClientFetch::start()
{
auto request = m_loader->request();
auto& options = m_loader->options();
auto referrer = request.httpReferrer();
cleanHTTPRequestHeadersForAccessControl(request, options.httpHeadersToKeep);
ASSERT(options.serviceWorkersMode != ServiceWorkersMode::None);
m_connection->startFetch(m_loader->identifier(), options.serviceWorkerRegistrationIdentifier.value(), request, options, referrer);
m_redirectionStatus = RedirectionStatus::None;
}
std::optional<ResourceError> ServiceWorkerClientFetch::validateResponse(const ResourceResponse& response)
{
if (response.type() == ResourceResponse::Type::Error)
return ResourceError { ResourceError::Type::General };
auto& options = m_loader->options();
if (options.mode != FetchOptions::Mode::NoCors && response.tainting() == ResourceResponse::Tainting::Opaque)
return ResourceError { errorDomainWebKitInternal, 0, response.url(), ASCIILiteral("Response served by service worker is opaque"), ResourceError::Type::AccessControl };
if (options.redirect != FetchOptions::Redirect::Manual && options.mode != FetchOptions::Mode::Navigate && response.tainting() == ResourceResponse::Tainting::Opaqueredirect)
return ResourceError { errorDomainWebKitInternal, 0, response.url(), ASCIILiteral("Response served by service worker is opaque redirect"), ResourceError::Type::AccessControl };
if ((options.redirect != FetchOptions::Redirect::Follow || options.mode == FetchOptions::Mode::Navigate) && response.isRedirected())
return ResourceError { errorDomainWebKitInternal, 0, response.url(), ASCIILiteral("Response served by service worker has redirections"), ResourceError::Type::AccessControl };
return std::nullopt;
}
void ServiceWorkerClientFetch::didReceiveResponse(ResourceResponse&& response)
{
callOnMainThread([this, protectedThis = makeRef(*this), response = WTFMove(response)]() mutable {
if (!m_loader)
return;
if (auto error = validateResponse(response)) {
m_loader->didFail(error.value());
if (auto callback = WTFMove(m_callback))
callback(Result::Succeeded);
return;
}
response.setSource(ResourceResponse::Source::ServiceWorker);
if (response.isRedirection() && response.httpHeaderFields().contains(HTTPHeaderName::Location)) {
m_redirectionStatus = RedirectionStatus::Receiving;
m_loader->willSendRequest(m_loader->request().redirectedRequest(response, m_shouldClearReferrerOnHTTPSToHTTPRedirect), response, [protectedThis = makeRef(*this), this](ResourceRequest&& request) {
if (request.isNull() || !m_callback)
return;
ASSERT(request == m_loader->request());
if (m_redirectionStatus == RedirectionStatus::Received) {
start();
return;
}
m_redirectionStatus = RedirectionStatus::Following;
});
return;
}
if (m_loader->originalRequest().requester() == ResourceRequest::Requester::Main) {
if (response.mimeType() == defaultMIMEType()) {
response.setMimeType(ASCIILiteral("text/html"));
response.setTextEncodingName(ASCIILiteral("UTF-8"));
}
}
if (response.url().isNull())
response.setURL(m_loader->request().url());
m_loader->didReceiveResponse(response);
if (auto callback = WTFMove(m_callback))
callback(Result::Succeeded);
});
}
void ServiceWorkerClientFetch::didReceiveData(const IPC::DataReference& data, int64_t encodedDataLength)
{
callOnMainThread([this, protectedThis = makeRef(*this), data = data.vector(), encodedDataLength] {
if (!m_loader)
return;
m_loader->didReceiveData(reinterpret_cast<const char*>(data.data()), data.size(), encodedDataLength, DataPayloadBytes);
});
}
void ServiceWorkerClientFetch::didReceiveFormData(const IPC::FormDataReference&)
{
}
void ServiceWorkerClientFetch::didFinish()
{
callOnMainThread([this, protectedThis = makeRef(*this)] {
if (!m_loader)
return;
switch (m_redirectionStatus) {
case RedirectionStatus::None:
break;
case RedirectionStatus::Receiving:
m_redirectionStatus = RedirectionStatus::Received;
return;
case RedirectionStatus::Following:
start();
return;
case RedirectionStatus::Received:
ASSERT_NOT_REACHED();
m_redirectionStatus = RedirectionStatus::None;
}
ASSERT(!m_callback);
m_loader->didFinishLoading(NetworkLoadMetrics { });
m_serviceWorkerProvider.fetchFinished(m_identifier);
});
}
void ServiceWorkerClientFetch::didFail()
{
callOnMainThread([this, protectedThis = makeRef(*this)] {
if (!m_loader)
return;
m_loader->didFail({ ResourceError::Type::General });
if (auto callback = WTFMove(m_callback))
callback(Result::Succeeded);
m_serviceWorkerProvider.fetchFinished(m_identifier);
});
}
void ServiceWorkerClientFetch::didNotHandle()
{
callOnMainThread([this, protectedThis = makeRef(*this)] {
if (!m_loader)
return;
if (auto callback = WTFMove(m_callback))
callback(Result::Unhandled);
m_serviceWorkerProvider.fetchFinished(m_identifier);
});
}
void ServiceWorkerClientFetch::cancel()
{
if (auto callback = WTFMove(m_callback))
callback(Result::Cancelled);
m_loader = nullptr;
}
}
#endif // ENABLE(SERVICE_WORKER)