NetworkDataTaskBlob.cpp [plain text]
#include "config.h"
#include "NetworkDataTaskBlob.h"
#if USE(NETWORK_SESSION)
#include "DataReference.h"
#include "Download.h"
#include "Logging.h"
#include "NetworkProcess.h"
#include "NetworkSession.h"
#include "WebErrors.h"
#include <WebCore/AsyncFileStream.h>
#include <WebCore/BlobData.h>
#include <WebCore/BlobRegistryImpl.h>
#include <WebCore/FileStream.h>
#include <WebCore/HTTPHeaderNames.h>
#include <WebCore/HTTPParsers.h>
#include <WebCore/ParsedContentRange.h>
#include <WebCore/ResourceError.h>
#include <WebCore/ResourceResponse.h>
#include <WebCore/SharedBuffer.h>
#include <WebCore/URL.h>
#include <wtf/MainThread.h>
#include <wtf/RunLoop.h>
using namespace WebCore;
namespace WebKit {
static const unsigned bufferSize = 512 * 1024;
static const int httpOK = 200;
static const int httpPartialContent = 206;
static const int httpNotAllowed = 403;
static const int httpRequestedRangeNotSatisfiable = 416;
static const int httpInternalError = 500;
static const char* httpOKText = "OK";
static const char* httpPartialContentText = "Partial Content";
static const char* httpNotAllowedText = "Not Allowed";
static const char* httpRequestedRangeNotSatisfiableText = "Requested Range Not Satisfiable";
static const char* httpInternalErrorText = "Internal Server Error";
static const char* const webKitBlobResourceDomain = "WebKitBlobResource";
NetworkDataTaskBlob::NetworkDataTaskBlob(NetworkSession& session, NetworkDataTaskClient& client, const ResourceRequest& request, ContentSniffingPolicy shouldContentSniff, const Vector<RefPtr<WebCore::BlobDataFileReference>>& fileReferences)
: NetworkDataTask(session, client, request, DoNotAllowStoredCredentials, false)
, m_stream(std::make_unique<AsyncFileStream>(*this))
, m_fileReferences(fileReferences)
{
for (auto& fileReference : m_fileReferences)
fileReference->prepareForFileAccess();
m_blobData = static_cast<BlobRegistryImpl&>(blobRegistry()).getBlobDataFromURL(request.url());
m_session->registerNetworkDataTask(*this);
LOG(NetworkSession, "%p - Created NetworkDataTaskBlob for %s", this, request.url().string().utf8().data());
}
NetworkDataTaskBlob::~NetworkDataTaskBlob()
{
for (auto& fileReference : m_fileReferences)
fileReference->revokeFileAccess();
clearStream();
m_session->unregisterNetworkDataTask(*this);
}
void NetworkDataTaskBlob::clearStream()
{
if (m_state == State::Completed)
return;
m_state = State::Completed;
if (m_fileOpened) {
m_fileOpened = false;
m_stream->close();
}
m_stream = nullptr;
}
void NetworkDataTaskBlob::resume()
{
ASSERT(m_state != State::Running);
if (m_state == State::Canceling || m_state == State::Completed)
return;
m_state = State::Running;
if (m_scheduledFailureType != NoFailure) {
ASSERT(m_failureTimer.isActive());
return;
}
RunLoop::main().dispatch([this, protectedThis = makeRef(*this)] {
if (m_state == State::Canceling || m_state == State::Completed || !m_client) {
clearStream();
return;
}
if (!equalLettersIgnoringASCIICase(m_firstRequest.httpMethod(), "get")) {
didFail(Error::MethodNotAllowed);
return;
}
if (!m_blobData) {
didFail(Error::NotFoundError);
return;
}
String range = m_firstRequest.httpHeaderField(HTTPHeaderName::Range);
if (!range.isEmpty() && !parseRange(range, m_rangeOffset, m_rangeEnd, m_rangeSuffixLength)) {
dispatchDidReceiveResponse(Error::RangeError);
return;
}
getSizeForNext();
});
}
void NetworkDataTaskBlob::suspend()
{
}
void NetworkDataTaskBlob::cancel()
{
if (m_state == State::Canceling || m_state == State::Completed)
return;
m_state = State::Canceling;
if (m_fileOpened) {
m_fileOpened = false;
m_stream->close();
}
if (isDownload())
cleanDownloadFiles();
}
void NetworkDataTaskBlob::invalidateAndCancel()
{
cancel();
clearStream();
}
void NetworkDataTaskBlob::getSizeForNext()
{
ASSERT(isMainThread());
if (m_sizeItemCount >= m_blobData->items().size()) {
seek();
dispatchDidReceiveResponse();
return;
}
const BlobDataItem& item = m_blobData->items().at(m_sizeItemCount);
switch (item.type()) {
case BlobDataItem::Type::Data:
didGetSize(item.length());
break;
case BlobDataItem::Type::File:
m_stream->getSize(item.file()->path(), item.file()->expectedModificationTime());
break;
default:
ASSERT_NOT_REACHED();
}
}
void NetworkDataTaskBlob::didGetSize(long long size)
{
ASSERT(isMainThread());
if (m_state == State::Canceling || m_state == State::Completed || (!m_client && !isDownload())) {
clearStream();
return;
}
if (size == -1) {
didFail(Error::NotFoundError);
return;
}
const BlobDataItem& item = m_blobData->items().at(m_sizeItemCount);
size = item.length();
m_itemLengthList.append(size);
m_totalSize += size;
m_totalRemainingSize += size;
m_sizeItemCount++;
getSizeForNext();
}
void NetworkDataTaskBlob::seek()
{
ASSERT(isMainThread());
if (m_rangeSuffixLength != kPositionNotSpecified) {
m_rangeOffset = m_totalRemainingSize - m_rangeSuffixLength;
m_rangeEnd = m_rangeOffset + m_rangeSuffixLength - 1;
}
if (m_rangeOffset == kPositionNotSpecified)
return;
long long offset = m_rangeOffset;
for (m_readItemCount = 0; m_readItemCount < m_blobData->items().size() && offset >= m_itemLengthList[m_readItemCount]; ++m_readItemCount)
offset -= m_itemLengthList[m_readItemCount];
m_currentItemReadSize = offset;
if (m_rangeEnd != kPositionNotSpecified) {
long long rangeSize = m_rangeEnd - m_rangeOffset + 1;
if (m_totalRemainingSize > rangeSize)
m_totalRemainingSize = rangeSize;
} else
m_totalRemainingSize -= m_rangeOffset;
}
void NetworkDataTaskBlob::dispatchDidReceiveResponse(Error errorCode)
{
LOG(NetworkSession, "%p - NetworkDataTaskBlob::dispatchDidReceiveResponse(%u)", this, static_cast<unsigned>(errorCode));
Ref<NetworkDataTaskBlob> protectedThis(*this);
ResourceResponse response(m_firstRequest.url(), errorCode != Error::NoError ? "text/plain" : m_blobData->contentType(), errorCode != Error::NoError ? 0 : m_totalRemainingSize, String());
switch (errorCode) {
case Error::NoError: {
bool isRangeRequest = m_rangeOffset != kPositionNotSpecified;
response.setHTTPStatusCode(isRangeRequest ? httpPartialContent : httpOK);
response.setHTTPStatusText(isRangeRequest ? httpPartialContentText : httpOKText);
response.setHTTPHeaderField(HTTPHeaderName::ContentType, m_blobData->contentType());
response.setHTTPHeaderField(HTTPHeaderName::ContentLength, String::number(m_totalRemainingSize));
if (isRangeRequest)
response.setHTTPHeaderField(HTTPHeaderName::ContentRange, ParsedContentRange(m_rangeOffset, m_rangeEnd, m_totalSize).headerValue());
break;
}
case Error::RangeError:
response.setHTTPStatusCode(httpRequestedRangeNotSatisfiable);
response.setHTTPStatusText(httpRequestedRangeNotSatisfiableText);
break;
case Error::SecurityError:
response.setHTTPStatusCode(httpNotAllowed);
response.setHTTPStatusText(httpNotAllowedText);
break;
default:
response.setHTTPStatusCode(httpInternalError);
response.setHTTPStatusText(httpInternalErrorText);
break;
}
didReceiveResponse(WTFMove(response), [this, protectedThis = WTFMove(protectedThis), errorCode](PolicyAction policyAction) {
LOG(NetworkSession, "%p - NetworkDataTaskBlob::didReceiveResponse completionHandler (%u)", this, static_cast<unsigned>(policyAction));
if (m_state == State::Canceling || m_state == State::Completed) {
clearStream();
return;
}
if (errorCode != Error::NoError) {
didFinish();
return;
}
switch (policyAction) {
case PolicyAction::PolicyUse:
m_buffer.resize(bufferSize);
read();
break;
case PolicyAction::PolicyIgnore:
break;
case PolicyAction::PolicyDownload:
download();
break;
}
});
}
void NetworkDataTaskBlob::read()
{
ASSERT(isMainThread());
if (!m_totalRemainingSize || m_readItemCount >= m_blobData->items().size()) {
didFinish();
return;
}
const BlobDataItem& item = m_blobData->items().at(m_readItemCount);
if (item.type() == BlobDataItem::Type::Data)
readData(item);
else if (item.type() == BlobDataItem::Type::File)
readFile(item);
else
ASSERT_NOT_REACHED();
}
void NetworkDataTaskBlob::readData(const BlobDataItem& item)
{
ASSERT(item.data().data());
long long bytesToRead = item.length() - m_currentItemReadSize;
if (bytesToRead > m_totalRemainingSize)
bytesToRead = m_totalRemainingSize;
consumeData(reinterpret_cast<const char*>(item.data().data()->data()) + item.offset() + m_currentItemReadSize, static_cast<int>(bytesToRead));
m_currentItemReadSize = 0;
}
void NetworkDataTaskBlob::readFile(const BlobDataItem& item)
{
ASSERT(m_stream);
if (m_fileOpened) {
m_stream->read(m_buffer.data(), m_buffer.size());
return;
}
long long bytesToRead = m_itemLengthList[m_readItemCount] - m_currentItemReadSize;
if (bytesToRead > m_totalRemainingSize)
bytesToRead = static_cast<int>(m_totalRemainingSize);
m_stream->openForRead(item.file()->path(), item.offset() + m_currentItemReadSize, bytesToRead);
m_fileOpened = true;
m_currentItemReadSize = 0;
}
void NetworkDataTaskBlob::didOpen(bool success)
{
if (m_state == State::Canceling || m_state == State::Completed || (!m_client && !isDownload())) {
clearStream();
return;
}
if (!success) {
didFail(Error::NotReadableError);
return;
}
Ref<NetworkDataTaskBlob> protectedThis(*this);
read();
}
void NetworkDataTaskBlob::didRead(int bytesRead)
{
if (m_state == State::Canceling || m_state == State::Completed || (!m_client && !isDownload())) {
clearStream();
return;
}
if (bytesRead < 0) {
didFail(Error::NotReadableError);
return;
}
Ref<NetworkDataTaskBlob> protectedThis(*this);
consumeData(m_buffer.data(), bytesRead);
}
void NetworkDataTaskBlob::consumeData(const char* data, int bytesRead)
{
m_totalRemainingSize -= bytesRead;
if (bytesRead) {
if (m_downloadFile != invalidPlatformFileHandle) {
if (!writeDownload(data, bytesRead))
return;
} else {
ASSERT(m_client);
m_client->didReceiveData(SharedBuffer::create(data, bytesRead));
}
}
if (m_fileOpened) {
if (!bytesRead) {
m_fileOpened = false;
m_stream->close();
m_readItemCount++;
}
} else {
m_readItemCount++;
}
read();
}
void NetworkDataTaskBlob::setPendingDownloadLocation(const String& filename, const SandboxExtension::Handle& sandboxExtensionHandle, bool allowOverwrite)
{
NetworkDataTask::setPendingDownloadLocation(filename, sandboxExtensionHandle, allowOverwrite);
ASSERT(!m_sandboxExtension);
m_sandboxExtension = SandboxExtension::create(sandboxExtensionHandle);
if (m_sandboxExtension)
m_sandboxExtension->consume();
if (allowOverwrite && fileExists(m_pendingDownloadLocation))
deleteFile(m_pendingDownloadLocation);
}
String NetworkDataTaskBlob::suggestedFilename() const
{
if (!m_suggestedFilename.isEmpty())
return m_suggestedFilename;
return ASCIILiteral("unknown");
}
void NetworkDataTaskBlob::download()
{
ASSERT(isDownload());
ASSERT(m_pendingDownloadLocation);
LOG(NetworkSession, "%p - NetworkDataTaskBlob::download to %s", this, m_pendingDownloadLocation.utf8().data());
m_downloadFile = openFile(m_pendingDownloadLocation, OpenForWrite);
if (m_downloadFile == invalidPlatformFileHandle) {
didFailDownload(cancelledError(m_firstRequest));
return;
}
auto& downloadManager = NetworkProcess::singleton().downloadManager();
auto download = std::make_unique<Download>(downloadManager, m_pendingDownloadID, *this, m_session->sessionID(), suggestedFilename());
auto* downloadPtr = download.get();
downloadManager.dataTaskBecameDownloadTask(m_pendingDownloadID, WTFMove(download));
downloadPtr->didCreateDestination(m_pendingDownloadLocation);
ASSERT(!m_client);
m_buffer.resize(bufferSize);
read();
}
bool NetworkDataTaskBlob::writeDownload(const char* data, int bytesRead)
{
ASSERT(isDownload());
int bytesWritten = writeToFile(m_downloadFile, data, bytesRead);
if (bytesWritten == -1) {
didFailDownload(cancelledError(m_firstRequest));
return false;
}
ASSERT(bytesWritten == bytesRead);
auto* download = NetworkProcess::singleton().downloadManager().download(m_pendingDownloadID);
ASSERT(download);
download->didReceiveData(bytesWritten);
return true;
}
void NetworkDataTaskBlob::cleanDownloadFiles()
{
if (m_downloadFile != invalidPlatformFileHandle) {
closeFile(m_downloadFile);
m_downloadFile = invalidPlatformFileHandle;
}
deleteFile(m_pendingDownloadLocation);
}
void NetworkDataTaskBlob::didFailDownload(const ResourceError& error)
{
LOG(NetworkSession, "%p - NetworkDataTaskBlob::didFailDownload", this);
clearStream();
cleanDownloadFiles();
if (m_sandboxExtension) {
m_sandboxExtension->revoke();
m_sandboxExtension = nullptr;
}
if (m_client)
m_client->didCompleteWithError(error);
else {
auto* download = NetworkProcess::singleton().downloadManager().download(m_pendingDownloadID);
ASSERT(download);
download->didFail(error, IPC::DataReference());
}
}
void NetworkDataTaskBlob::didFinishDownload()
{
LOG(NetworkSession, "%p - NetworkDataTaskBlob::didFinishDownload", this);
ASSERT(isDownload());
closeFile(m_downloadFile);
m_downloadFile = invalidPlatformFileHandle;
if (m_sandboxExtension) {
m_sandboxExtension->revoke();
m_sandboxExtension = nullptr;
}
clearStream();
auto* download = NetworkProcess::singleton().downloadManager().download(m_pendingDownloadID);
ASSERT(download);
download->didFinish();
}
void NetworkDataTaskBlob::didFail(Error errorCode)
{
ASSERT(!m_sandboxExtension);
Ref<NetworkDataTaskBlob> protectedThis(*this);
if (isDownload()) {
didFailDownload(ResourceError(webKitBlobResourceDomain, static_cast<int>(errorCode), m_firstRequest.url(), String()));
return;
}
LOG(NetworkSession, "%p - NetworkDataTaskBlob::didFail", this);
clearStream();
ASSERT(m_client);
m_client->didCompleteWithError(ResourceError(webKitBlobResourceDomain, static_cast<int>(errorCode), m_firstRequest.url(), String()));
}
void NetworkDataTaskBlob::didFinish()
{
if (m_downloadFile != invalidPlatformFileHandle) {
didFinishDownload();
return;
}
ASSERT(!m_sandboxExtension);
LOG(NetworkSession, "%p - NetworkDataTaskBlob::didFinish", this);
clearStream();
ASSERT(m_client);
m_client->didCompleteWithError({ });
}
}
#endif // USE(NETWORK_SESSION)