blob: 9051431621a8c108a6ef0f51eb60f774afcc0a11 [file] [log] [blame]
jianli@chromium.org4c6124f2010-08-23 20:09:56 +00001/*
2 * Copyright (C) 2010 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#include "config.h"
32
jianli@chromium.org4c6124f2010-08-23 20:09:56 +000033#include "BlobResourceHandle.h"
34
35#include "AsyncFileStream.h"
ap@apple.combbc1eb52014-05-06 22:11:07 +000036#include "BlobData.h"
jianli@chromium.org4c6124f2010-08-23 20:09:56 +000037#include "FileStream.h"
andersca@apple.coma96cf712014-06-14 17:49:44 +000038#include "HTTPHeaderNames.h"
jianli@chromium.org4c6124f2010-08-23 20:09:56 +000039#include "HTTPParsers.h"
jer.noble@apple.com382a0022016-01-28 19:17:17 +000040#include "ParsedContentRange.h"
jianli@chromium.org4c6124f2010-08-23 20:09:56 +000041#include "ResourceError.h"
ap@apple.coma4afd532012-03-01 18:06:06 +000042#include "ResourceHandleClient.h"
jianli@chromium.org4c6124f2010-08-23 20:09:56 +000043#include "ResourceRequest.h"
44#include "ResourceResponse.h"
ap@apple.come6543bf2013-04-10 00:04:55 +000045#include "SharedBuffer.h"
achristensen@apple.comafdd08f2017-11-03 02:54:36 +000046#include <wtf/CompletionHandler.h>
ross.kirsling@sony.com50355432019-01-24 17:25:57 +000047#include <wtf/FileSystem.h>
paroga@webkit.orgc309e0e2011-07-31 02:23:31 +000048#include <wtf/MainThread.h>
akling@apple.comf8515982013-09-02 18:50:01 +000049#include <wtf/Ref.h>
ross.kirsling@sony.com50355432019-01-24 17:25:57 +000050#include <wtf/URL.h>
jianli@chromium.org4c6124f2010-08-23 20:09:56 +000051
52namespace WebCore {
53
ap@apple.com6eda53b2012-03-01 22:50:07 +000054static const unsigned bufferSize = 512 * 1024;
jianli@chromium.org4c6124f2010-08-23 20:09:56 +000055
56static const int httpOK = 200;
57static const int httpPartialContent = 206;
58static const int httpNotAllowed = 403;
jianli@chromium.org4c6124f2010-08-23 20:09:56 +000059static const int httpRequestedRangeNotSatisfiable = 416;
60static const int httpInternalError = 500;
61static const char* httpOKText = "OK";
62static const char* httpPartialContentText = "Partial Content";
63static const char* httpNotAllowedText = "Not Allowed";
jianli@chromium.org4c6124f2010-08-23 20:09:56 +000064static const char* httpRequestedRangeNotSatisfiableText = "Requested Range Not Satisfiable";
65static const char* httpInternalErrorText = "Internal Server Error";
66
ap@apple.comb2852ac2012-02-28 19:49:54 +000067static const char* const webKitBlobResourceDomain = "WebKitBlobResource";
jianli@chromium.org4c6124f2010-08-23 20:09:56 +000068
69///////////////////////////////////////////////////////////////////////////////
70// BlobResourceSynchronousLoader
71
72namespace {
73
74class BlobResourceSynchronousLoader : public ResourceHandleClient {
75public:
76 BlobResourceSynchronousLoader(ResourceError&, ResourceResponse&, Vector<char>&);
77
achristensen@apple.comcceb9b42018-01-23 23:22:19 +000078 void didReceiveResponseAsync(ResourceHandle*, ResourceResponse&&, CompletionHandler<void()>&&) final;
achristensen@apple.comee6c1f42017-11-01 01:13:04 +000079 void didFail(ResourceHandle*, const ResourceError&) final;
achristensen@apple.comafdd08f2017-11-03 02:54:36 +000080 void willSendRequestAsync(ResourceHandle*, ResourceRequest&&, ResourceResponse&&, CompletionHandler<void(ResourceRequest&&)>&&) final;
achristensen@apple.comee6c1f42017-11-01 01:13:04 +000081#if USE(PROTECTION_SPACE_AUTH_CALLBACK)
achristensen@apple.com59532342018-03-25 06:19:02 +000082 void canAuthenticateAgainstProtectionSpaceAsync(ResourceHandle*, const ProtectionSpace&, CompletionHandler<void(bool)>&&) final;
achristensen@apple.comee6c1f42017-11-01 01:13:04 +000083#endif
jianli@chromium.org4c6124f2010-08-23 20:09:56 +000084
85private:
86 ResourceError& m_error;
87 ResourceResponse& m_response;
88 Vector<char>& m_data;
89};
90
91BlobResourceSynchronousLoader::BlobResourceSynchronousLoader(ResourceError& error, ResourceResponse& response, Vector<char>& data)
92 : m_error(error)
93 , m_response(response)
94 , m_data(data)
95{
96}
97
achristensen@apple.comafdd08f2017-11-03 02:54:36 +000098void BlobResourceSynchronousLoader::willSendRequestAsync(ResourceHandle*, ResourceRequest&& request, ResourceResponse&&, CompletionHandler<void(ResourceRequest&&)>&& completionHandler)
achristensen@apple.comee6c1f42017-11-01 01:13:04 +000099{
100 ASSERT_NOT_REACHED();
achristensen@apple.comafdd08f2017-11-03 02:54:36 +0000101 completionHandler(WTFMove(request));
achristensen@apple.comee6c1f42017-11-01 01:13:04 +0000102}
103
104#if USE(PROTECTION_SPACE_AUTH_CALLBACK)
achristensen@apple.com59532342018-03-25 06:19:02 +0000105void BlobResourceSynchronousLoader::canAuthenticateAgainstProtectionSpaceAsync(ResourceHandle*, const ProtectionSpace&, CompletionHandler<void(bool)>&& completionHandler)
achristensen@apple.comee6c1f42017-11-01 01:13:04 +0000106{
107 ASSERT_NOT_REACHED();
achristensen@apple.com59532342018-03-25 06:19:02 +0000108 completionHandler(false);
achristensen@apple.comee6c1f42017-11-01 01:13:04 +0000109}
110#endif
111
achristensen@apple.comcceb9b42018-01-23 23:22:19 +0000112void BlobResourceSynchronousLoader::didReceiveResponseAsync(ResourceHandle* handle, ResourceResponse&& response, CompletionHandler<void()>&& completionHandler)
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000113{
114 // We cannot handle the size that is more than maximum integer.
ap@apple.comb2852ac2012-02-28 19:49:54 +0000115 if (response.expectedContentLength() > INT_MAX) {
commit-queue@webkit.org97d42cd2016-08-30 18:22:15 +0000116 m_error = ResourceError(webKitBlobResourceDomain, static_cast<int>(BlobResourceHandle::Error::NotReadableError), response.url(), "File is too large");
achristensen@apple.comcceb9b42018-01-23 23:22:19 +0000117 completionHandler();
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000118 return;
119 }
120
121 m_response = response;
122
123 // Read all the data.
124 m_data.resize(static_cast<size_t>(response.expectedContentLength()));
125 static_cast<BlobResourceHandle*>(handle)->readSync(m_data.data(), static_cast<int>(m_data.size()));
achristensen@apple.comcceb9b42018-01-23 23:22:19 +0000126 completionHandler();
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000127}
128
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000129void BlobResourceSynchronousLoader::didFail(ResourceHandle*, const ResourceError& error)
130{
131 m_error = error;
132}
133
134}
135
136///////////////////////////////////////////////////////////////////////////////
137// BlobResourceHandle
138
achristensen@apple.com0faecda2016-06-07 18:52:06 +0000139Ref<BlobResourceHandle> BlobResourceHandle::createAsync(BlobData* blobData, const ResourceRequest& request, ResourceHandleClient* client)
ap@apple.combb785b62013-02-21 08:27:10 +0000140{
achristensen@apple.com0faecda2016-06-07 18:52:06 +0000141 return adoptRef(*new BlobResourceHandle(blobData, request, client, true));
ap@apple.combb785b62013-02-21 08:27:10 +0000142}
143
ap@apple.combbc1eb52014-05-06 22:11:07 +0000144void BlobResourceHandle::loadResourceSynchronously(BlobData* blobData, const ResourceRequest& request, ResourceError& error, ResourceResponse& response, Vector<char>& data)
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000145{
darin@apple.com048cc3a2016-01-22 17:17:04 +0000146 if (!equalLettersIgnoringASCIICase(request.httpMethod(), "get")) {
commit-queue@webkit.org97d42cd2016-08-30 18:22:15 +0000147 error = ResourceError(webKitBlobResourceDomain, static_cast<int>(Error::MethodNotAllowed), response.url(), "Request method must be GET");
ap@apple.combb785b62013-02-21 08:27:10 +0000148 return;
149 }
150
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000151 BlobResourceSynchronousLoader loader(error, response, data);
ap@apple.combb785b62013-02-21 08:27:10 +0000152 RefPtr<BlobResourceHandle> handle = adoptRef(new BlobResourceHandle(blobData, request, &loader, false));
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000153 handle->start();
154}
155
ap@apple.combbc1eb52014-05-06 22:11:07 +0000156BlobResourceHandle::BlobResourceHandle(BlobData* blobData, const ResourceRequest& request, ResourceHandleClient* client, bool async)
dbates@webkit.org8c69e062017-11-01 22:23:41 +0000157 : ResourceHandle { nullptr, request, client, false /* defersLoading */, false /* shouldContentSniff */, true /* shouldContentEncodingSniff */ }
158 , m_blobData { blobData }
159 , m_async { async }
ap@apple.com6bb2a242013-03-05 17:42:47 +0000160{
jianli@chromium.org859b5482011-02-07 23:28:35 +0000161 if (m_async)
ysuzuki@apple.com1d8e24d2019-08-19 06:59:40 +0000162 m_asyncStream = makeUnique<AsyncFileStream>(*this);
jianli@chromium.org859b5482011-02-07 23:28:35 +0000163 else
ysuzuki@apple.com1d8e24d2019-08-19 06:59:40 +0000164 m_stream = makeUnique<FileStream>();
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000165}
166
dbates@webkit.orgf21f3ae2017-10-19 23:48:45 +0000167BlobResourceHandle::~BlobResourceHandle() = default;
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000168
169void BlobResourceHandle::cancel()
170{
darin@apple.comc2c1a3e2014-11-09 01:01:22 +0000171 m_asyncStream = nullptr;
cdumez@apple.come5484312016-11-10 05:28:53 +0000172 m_fileOpened = false;
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000173
174 m_aborted = true;
jianli@chromium.org859b5482011-02-07 23:28:35 +0000175
176 ResourceHandle::cancel();
177}
178
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000179void BlobResourceHandle::start()
180{
andersca@apple.com79e754c2014-06-13 23:01:31 +0000181 if (!m_async) {
182 doStart();
jianli@chromium.org859b5482011-02-07 23:28:35 +0000183 return;
184 }
185
andersca@apple.com79e754c2014-06-13 23:01:31 +0000186 // Finish this async call quickly and return.
ggaren@apple.com243eacc2016-07-15 21:51:07 +0000187 callOnMainThread([protectedThis = makeRef(*this)]() mutable {
beidson@apple.com7bd3d3b2016-05-13 23:42:17 +0000188 protectedThis->doStart();
andersca@apple.com79e754c2014-06-13 23:01:31 +0000189 });
jianli@chromium.org859b5482011-02-07 23:28:35 +0000190}
191
192void BlobResourceHandle::doStart()
193{
ap@apple.combbc1eb52014-05-06 22:11:07 +0000194 ASSERT(isMainThread());
195
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000196 // Do not continue if the request is aborted or an error occurs.
commit-queue@webkit.org97d42cd2016-08-30 18:22:15 +0000197 if (erroredOrAborted())
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000198 return;
199
youenn.fablet@crf.canon.frf7841df2016-03-25 14:19:31 +0000200 if (!equalLettersIgnoringASCIICase(firstRequest().httpMethod(), "get")) {
commit-queue@webkit.org97d42cd2016-08-30 18:22:15 +0000201 notifyFail(Error::MethodNotAllowed);
youenn.fablet@crf.canon.frf7841df2016-03-25 14:19:31 +0000202 return;
203 }
204
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000205 // If the blob data is not found, fail now.
206 if (!m_blobData) {
commit-queue@webkit.org97d42cd2016-08-30 18:22:15 +0000207 notifyFail(Error::NotFoundError);
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000208 return;
209 }
210
211 // Parse the "Range" header we care about.
andersca@apple.coma96cf712014-06-14 17:49:44 +0000212 String range = firstRequest().httpHeaderField(HTTPHeaderName::Range);
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000213 if (!range.isEmpty() && !parseRange(range, m_rangeOffset, m_rangeEnd, m_rangeSuffixLength)) {
commit-queue@webkit.org97d42cd2016-08-30 18:22:15 +0000214 m_errorCode = Error::RangeError;
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000215 notifyResponse();
216 return;
217 }
218
219 if (m_async)
220 getSizeForNext();
221 else {
beidson@apple.com7bd3d3b2016-05-13 23:42:17 +0000222 Ref<BlobResourceHandle> protectedThis(*this); // getSizeForNext calls the client
commit-queue@webkit.org97d42cd2016-08-30 18:22:15 +0000223 for (size_t i = 0; i < m_blobData->items().size() && !erroredOrAborted(); ++i)
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000224 getSizeForNext();
225 notifyResponse();
226 }
227}
228
229void BlobResourceHandle::getSizeForNext()
230{
ap@apple.combbc1eb52014-05-06 22:11:07 +0000231 ASSERT(isMainThread());
232
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000233 // Do we finish validating and counting size for all items?
234 if (m_sizeItemCount >= m_blobData->items().size()) {
235 seek();
236
237 // Start reading if in asynchronous mode.
238 if (m_async) {
beidson@apple.com7bd3d3b2016-05-13 23:42:17 +0000239 Ref<BlobResourceHandle> protectedThis(*this);
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000240 notifyResponse();
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000241 }
242 return;
243 }
244
245 const BlobDataItem& item = m_blobData->items().at(m_sizeItemCount);
beidson@apple.com26c1d622016-03-31 01:21:04 +0000246 switch (item.type()) {
247 case BlobDataItem::Type::Data:
ap@apple.com6901a792014-05-06 23:34:35 +0000248 didGetSize(item.length());
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000249 break;
beidson@apple.com26c1d622016-03-31 01:21:04 +0000250 case BlobDataItem::Type::File:
ap@apple.com6901a792014-05-06 23:34:35 +0000251 // Files know their sizes, but asking the stream to verify that the file wasn't modified.
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000252 if (m_async)
beidson@apple.com26c1d622016-03-31 01:21:04 +0000253 m_asyncStream->getSize(item.file()->path(), item.file()->expectedModificationTime());
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000254 else
beidson@apple.com26c1d622016-03-31 01:21:04 +0000255 didGetSize(m_stream->getSize(item.file()->path(), item.file()->expectedModificationTime()));
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000256 break;
257 default:
258 ASSERT_NOT_REACHED();
259 }
260}
261
262void BlobResourceHandle::didGetSize(long long size)
263{
ap@apple.combbc1eb52014-05-06 22:11:07 +0000264 ASSERT(isMainThread());
265
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000266 // Do not continue if the request is aborted or an error occurs.
commit-queue@webkit.org97d42cd2016-08-30 18:22:15 +0000267 if (erroredOrAborted())
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000268 return;
269
270 // If the size is -1, it means the file has been moved or changed. Fail now.
271 if (size == -1) {
commit-queue@webkit.org97d42cd2016-08-30 18:22:15 +0000272 notifyFail(Error::NotFoundError);
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000273 return;
274 }
275
276 // The size passed back is the size of the whole file. If the underlying item is a sliced file, we need to use the slice length.
277 const BlobDataItem& item = m_blobData->items().at(m_sizeItemCount);
ap@apple.com6901a792014-05-06 23:34:35 +0000278 size = item.length();
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000279
280 // Cache the size.
281 m_itemLengthList.append(size);
282
283 // Count the size.
jer.noble@apple.com382a0022016-01-28 19:17:17 +0000284 m_totalSize += size;
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000285 m_totalRemainingSize += size;
286 m_sizeItemCount++;
287
288 // Continue with the next item.
289 getSizeForNext();
290}
291
292void BlobResourceHandle::seek()
293{
ap@apple.combbc1eb52014-05-06 22:11:07 +0000294 ASSERT(isMainThread());
295
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000296 // Convert from the suffix length to the range.
jer.noble@apple.com382a0022016-01-28 19:17:17 +0000297 if (m_rangeSuffixLength != kPositionNotSpecified) {
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000298 m_rangeOffset = m_totalRemainingSize - m_rangeSuffixLength;
299 m_rangeEnd = m_rangeOffset + m_rangeSuffixLength - 1;
300 }
301
302 // Bail out if the range is not provided.
jer.noble@apple.com382a0022016-01-28 19:17:17 +0000303 if (m_rangeOffset == kPositionNotSpecified)
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000304 return;
305
306 // Skip the initial items that are not in the range.
307 long long offset = m_rangeOffset;
308 for (m_readItemCount = 0; m_readItemCount < m_blobData->items().size() && offset >= m_itemLengthList[m_readItemCount]; ++m_readItemCount)
309 offset -= m_itemLengthList[m_readItemCount];
310
311 // Set the offset that need to jump to for the first item in the range.
312 m_currentItemReadSize = offset;
313
314 // Adjust the total remaining size in order not to go beyond the range.
jer.noble@apple.com382a0022016-01-28 19:17:17 +0000315 if (m_rangeEnd != kPositionNotSpecified) {
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000316 long long rangeSize = m_rangeEnd - m_rangeOffset + 1;
317 if (m_totalRemainingSize > rangeSize)
318 m_totalRemainingSize = rangeSize;
319 } else
320 m_totalRemainingSize -= m_rangeOffset;
321}
322
323int BlobResourceHandle::readSync(char* buf, int length)
324{
ap@apple.combbc1eb52014-05-06 22:11:07 +0000325 ASSERT(isMainThread());
326
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000327 ASSERT(!m_async);
beidson@apple.com7bd3d3b2016-05-13 23:42:17 +0000328 Ref<BlobResourceHandle> protectedThis(*this);
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000329
330 int offset = 0;
331 int remaining = length;
332 while (remaining) {
333 // Do not continue if the request is aborted or an error occurs.
commit-queue@webkit.org97d42cd2016-08-30 18:22:15 +0000334 if (erroredOrAborted())
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000335 break;
336
337 // If there is no more remaining data to read, we are done.
338 if (!m_totalRemainingSize || m_readItemCount >= m_blobData->items().size())
339 break;
commit-queue@webkit.org97d42cd2016-08-30 18:22:15 +0000340
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000341 const BlobDataItem& item = m_blobData->items().at(m_readItemCount);
342 int bytesRead = 0;
beidson@apple.com26c1d622016-03-31 01:21:04 +0000343 if (item.type() == BlobDataItem::Type::Data)
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000344 bytesRead = readDataSync(item, buf + offset, remaining);
beidson@apple.com26c1d622016-03-31 01:21:04 +0000345 else if (item.type() == BlobDataItem::Type::File)
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000346 bytesRead = readFileSync(item, buf + offset, remaining);
347 else
348 ASSERT_NOT_REACHED();
349
350 if (bytesRead > 0) {
351 offset += bytesRead;
352 remaining -= bytesRead;
353 }
354 }
355
356 int result;
commit-queue@webkit.org97d42cd2016-08-30 18:22:15 +0000357 if (erroredOrAborted())
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000358 result = -1;
359 else
360 result = length - remaining;
361
ap@apple.come6543bf2013-04-10 00:04:55 +0000362 if (result > 0)
363 notifyReceiveData(buf, result);
364
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000365 if (!result)
366 notifyFinish();
367
368 return result;
369}
370
371int BlobResourceHandle::readDataSync(const BlobDataItem& item, char* buf, int length)
372{
ap@apple.combbc1eb52014-05-06 22:11:07 +0000373 ASSERT(isMainThread());
374
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000375 ASSERT(!m_async);
376
ap@apple.com6901a792014-05-06 23:34:35 +0000377 long long remaining = item.length() - m_currentItemReadSize;
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000378 int bytesToRead = (length > remaining) ? static_cast<int>(remaining) : length;
379 if (bytesToRead > m_totalRemainingSize)
380 bytesToRead = static_cast<int>(m_totalRemainingSize);
beidson@apple.com26c1d622016-03-31 01:21:04 +0000381 memcpy(buf, item.data().data() + item.offset() + m_currentItemReadSize, bytesToRead);
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000382 m_totalRemainingSize -= bytesToRead;
383
384 m_currentItemReadSize += bytesToRead;
ap@apple.com6901a792014-05-06 23:34:35 +0000385 if (m_currentItemReadSize == item.length()) {
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000386 m_readItemCount++;
387 m_currentItemReadSize = 0;
388 }
389
390 return bytesToRead;
391}
392
393int BlobResourceHandle::readFileSync(const BlobDataItem& item, char* buf, int length)
394{
ap@apple.combbc1eb52014-05-06 22:11:07 +0000395 ASSERT(isMainThread());
396
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000397 ASSERT(!m_async);
398
399 if (!m_fileOpened) {
400 long long bytesToRead = m_itemLengthList[m_readItemCount] - m_currentItemReadSize;
401 if (bytesToRead > m_totalRemainingSize)
402 bytesToRead = m_totalRemainingSize;
beidson@apple.com26c1d622016-03-31 01:21:04 +0000403 bool success = m_stream->openForRead(item.file()->path(), item.offset() + m_currentItemReadSize, bytesToRead);
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000404 m_currentItemReadSize = 0;
405 if (!success) {
commit-queue@webkit.org97d42cd2016-08-30 18:22:15 +0000406 m_errorCode = Error::NotReadableError;
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000407 return 0;
408 }
409
410 m_fileOpened = true;
411 }
412
413 int bytesRead = m_stream->read(buf, length);
414 if (bytesRead < 0) {
commit-queue@webkit.org97d42cd2016-08-30 18:22:15 +0000415 m_errorCode = Error::NotReadableError;
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000416 return 0;
417 }
418 if (!bytesRead) {
419 m_stream->close();
420 m_fileOpened = false;
421 m_readItemCount++;
422 } else
423 m_totalRemainingSize -= bytesRead;
424
425 return bytesRead;
426}
427
428void BlobResourceHandle::readAsync()
429{
ap@apple.combbc1eb52014-05-06 22:11:07 +0000430 ASSERT(isMainThread());
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000431
432 // Do not continue if the request is aborted or an error occurs.
commit-queue@webkit.org97d42cd2016-08-30 18:22:15 +0000433 if (erroredOrAborted())
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000434 return;
435
436 // If there is no more remaining data to read, we are done.
437 if (!m_totalRemainingSize || m_readItemCount >= m_blobData->items().size()) {
438 notifyFinish();
439 return;
440 }
441
442 const BlobDataItem& item = m_blobData->items().at(m_readItemCount);
beidson@apple.com26c1d622016-03-31 01:21:04 +0000443 if (item.type() == BlobDataItem::Type::Data)
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000444 readDataAsync(item);
beidson@apple.com26c1d622016-03-31 01:21:04 +0000445 else if (item.type() == BlobDataItem::Type::File)
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000446 readFileAsync(item);
447 else
448 ASSERT_NOT_REACHED();
449}
450
451void BlobResourceHandle::readDataAsync(const BlobDataItem& item)
452{
ap@apple.combbc1eb52014-05-06 22:11:07 +0000453 ASSERT(isMainThread());
beidson@apple.com26c1d622016-03-31 01:21:04 +0000454 ASSERT(item.data().data());
455
beidson@apple.com7bd3d3b2016-05-13 23:42:17 +0000456 Ref<BlobResourceHandle> protectedThis(*this);
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000457
ap@apple.com6901a792014-05-06 23:34:35 +0000458 long long bytesToRead = item.length() - m_currentItemReadSize;
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000459 if (bytesToRead > m_totalRemainingSize)
460 bytesToRead = m_totalRemainingSize;
beidson@apple.com26c1d622016-03-31 01:21:04 +0000461 consumeData(reinterpret_cast<const char*>(item.data().data()->data()) + item.offset() + m_currentItemReadSize, static_cast<int>(bytesToRead));
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000462 m_currentItemReadSize = 0;
463}
464
465void BlobResourceHandle::readFileAsync(const BlobDataItem& item)
466{
ap@apple.combbc1eb52014-05-06 22:11:07 +0000467 ASSERT(isMainThread());
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000468
469 if (m_fileOpened) {
470 m_asyncStream->read(m_buffer.data(), m_buffer.size());
471 return;
472 }
473
474 long long bytesToRead = m_itemLengthList[m_readItemCount] - m_currentItemReadSize;
475 if (bytesToRead > m_totalRemainingSize)
476 bytesToRead = static_cast<int>(m_totalRemainingSize);
beidson@apple.com26c1d622016-03-31 01:21:04 +0000477 m_asyncStream->openForRead(item.file()->path(), item.offset() + m_currentItemReadSize, bytesToRead);
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000478 m_fileOpened = true;
479 m_currentItemReadSize = 0;
480}
481
482void BlobResourceHandle::didOpen(bool success)
483{
484 ASSERT(m_async);
485
486 if (!success) {
commit-queue@webkit.org97d42cd2016-08-30 18:22:15 +0000487 failed(Error::NotReadableError);
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000488 return;
489 }
490
491 // Continue the reading.
492 readAsync();
493}
494
495void BlobResourceHandle::didRead(int bytesRead)
496{
ap@apple.comb2852ac2012-02-28 19:49:54 +0000497 if (bytesRead < 0) {
commit-queue@webkit.org97d42cd2016-08-30 18:22:15 +0000498 failed(Error::NotReadableError);
ap@apple.comb2852ac2012-02-28 19:49:54 +0000499 return;
500 }
501
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000502 consumeData(m_buffer.data(), bytesRead);
503}
504
505void BlobResourceHandle::consumeData(const char* data, int bytesRead)
506{
507 ASSERT(m_async);
beidson@apple.com7bd3d3b2016-05-13 23:42:17 +0000508 Ref<BlobResourceHandle> protectedThis(*this);
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000509
510 m_totalRemainingSize -= bytesRead;
511
512 // Notify the client.
513 if (bytesRead)
514 notifyReceiveData(data, bytesRead);
515
516 if (m_fileOpened) {
517 // When the current item is a file item, the reading is completed only if bytesRead is 0.
518 if (!bytesRead) {
519 // Close the file.
520 m_fileOpened = false;
521 m_asyncStream->close();
522
523 // Move to the next item.
524 m_readItemCount++;
525 }
526 } else {
527 // Otherwise, we read the current text item as a whole and move to the next item.
528 m_readItemCount++;
529 }
530
531 // Continue the reading.
532 readAsync();
533}
534
commit-queue@webkit.org97d42cd2016-08-30 18:22:15 +0000535void BlobResourceHandle::failed(Error errorCode)
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000536{
537 ASSERT(m_async);
beidson@apple.com7bd3d3b2016-05-13 23:42:17 +0000538 Ref<BlobResourceHandle> protectedThis(*this);
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000539
540 // Notify the client.
541 notifyFail(errorCode);
542
543 // Close the file if needed.
544 if (m_fileOpened) {
545 m_fileOpened = false;
546 m_asyncStream->close();
547 }
548}
549
550void BlobResourceHandle::notifyResponse()
551{
552 if (!client())
553 return;
554
commit-queue@webkit.org97d42cd2016-08-30 18:22:15 +0000555 if (m_errorCode != Error::NoError) {
beidson@apple.com7bd3d3b2016-05-13 23:42:17 +0000556 Ref<BlobResourceHandle> protectedThis(*this);
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000557 notifyResponseOnError();
558 notifyFinish();
559 } else
560 notifyResponseOnSuccess();
561}
562
563void BlobResourceHandle::notifyResponseOnSuccess()
564{
ap@apple.combbc1eb52014-05-06 22:11:07 +0000565 ASSERT(isMainThread());
566
jer.noble@apple.com382a0022016-01-28 19:17:17 +0000567 bool isRangeRequest = m_rangeOffset != kPositionNotSpecified;
antti@apple.com021dc012014-09-04 20:00:11 +0000568 ResourceResponse response(firstRequest().url(), m_blobData->contentType(), m_totalRemainingSize, String());
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000569 response.setHTTPStatusCode(isRangeRequest ? httpPartialContent : httpOK);
570 response.setHTTPStatusText(isRangeRequest ? httpPartialContentText : httpOKText);
youenn.fablet@crf.canon.frf7841df2016-03-25 14:19:31 +0000571
572 response.setHTTPHeaderField(HTTPHeaderName::ContentType, m_blobData->contentType());
573 response.setHTTPHeaderField(HTTPHeaderName::ContentLength, String::number(m_totalRemainingSize));
574
jer.noble@apple.com382a0022016-01-28 19:17:17 +0000575 if (isRangeRequest)
576 response.setHTTPHeaderField(HTTPHeaderName::ContentRange, ParsedContentRange(m_rangeOffset, m_rangeEnd, m_totalSize).headerValue());
ap@apple.com0c1d49d2014-05-02 22:41:24 +0000577 // FIXME: If a resource identified with a blob: URL is a File object, user agents must use that file's name attribute,
578 // as if the response had a Content-Disposition header with the filename parameter set to the File's name attribute.
579 // Notably, this will affect a name suggested in "File Save As".
ap@apple.come6543bf2013-04-10 00:04:55 +0000580
achristensen@apple.comcceb9b42018-01-23 23:22:19 +0000581 client()->didReceiveResponseAsync(this, WTFMove(response), [this, protectedThis = makeRef(*this)] {
582 m_buffer.resize(bufferSize);
583 readAsync();
584 });
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000585}
586
587void BlobResourceHandle::notifyResponseOnError()
588{
commit-queue@webkit.org97d42cd2016-08-30 18:22:15 +0000589 ASSERT(m_errorCode != Error::NoError);
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000590
antti@apple.com021dc012014-09-04 20:00:11 +0000591 ResourceResponse response(firstRequest().url(), "text/plain", 0, String());
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000592 switch (m_errorCode) {
commit-queue@webkit.org97d42cd2016-08-30 18:22:15 +0000593 case Error::RangeError:
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000594 response.setHTTPStatusCode(httpRequestedRangeNotSatisfiable);
595 response.setHTTPStatusText(httpRequestedRangeNotSatisfiableText);
596 break;
commit-queue@webkit.org97d42cd2016-08-30 18:22:15 +0000597 case Error::SecurityError:
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000598 response.setHTTPStatusCode(httpNotAllowed);
599 response.setHTTPStatusText(httpNotAllowedText);
600 break;
601 default:
602 response.setHTTPStatusCode(httpInternalError);
603 response.setHTTPStatusText(httpInternalErrorText);
604 break;
605 }
ap@apple.come6543bf2013-04-10 00:04:55 +0000606
achristensen@apple.comcceb9b42018-01-23 23:22:19 +0000607 client()->didReceiveResponseAsync(this, WTFMove(response), [this, protectedThis = makeRef(*this)] {
608 m_buffer.resize(bufferSize);
609 readAsync();
610 });
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000611}
612
613void BlobResourceHandle::notifyReceiveData(const char* data, int bytesRead)
614{
615 if (client())
achristensen@apple.com8aff3d92017-04-24 17:31:05 +0000616 client()->didReceiveBuffer(this, SharedBuffer::create(reinterpret_cast<const uint8_t*>(data), bytesRead), bytesRead);
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000617}
618
commit-queue@webkit.org97d42cd2016-08-30 18:22:15 +0000619void BlobResourceHandle::notifyFail(Error errorCode)
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000620{
621 if (client())
commit-queue@webkit.org97d42cd2016-08-30 18:22:15 +0000622 client()->didFail(this, ResourceError(webKitBlobResourceDomain, static_cast<int>(errorCode), firstRequest().url(), String()));
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000623}
624
andersca@apple.com31366e82015-08-21 20:50:59 +0000625static void doNotifyFinish(BlobResourceHandle& handle)
jianli@chromium.org859b5482011-02-07 23:28:35 +0000626{
andersca@apple.com31366e82015-08-21 20:50:59 +0000627 if (handle.aborted())
628 return;
japhet@chromium.org28078752011-05-28 02:24:20 +0000629
andersca@apple.com31366e82015-08-21 20:50:59 +0000630 if (!handle.client())
631 return;
632
joepeck@webkit.orgff39b7b2017-02-25 05:48:51 +0000633 handle.client()->didFinishLoading(&handle);
jianli@chromium.org859b5482011-02-07 23:28:35 +0000634}
635
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000636void BlobResourceHandle::notifyFinish()
637{
andersca@apple.com31366e82015-08-21 20:50:59 +0000638 if (!m_async) {
639 doNotifyFinish(*this);
jianli@chromium.org859b5482011-02-07 23:28:35 +0000640 return;
641 }
642
andersca@apple.com31366e82015-08-21 20:50:59 +0000643 // Schedule to notify the client from a standalone function because the client might dispose the handle immediately from the callback function
644 // while we still have BlobResourceHandle calls in the stack.
ggaren@apple.com243eacc2016-07-15 21:51:07 +0000645 callOnMainThread([protectedThis = makeRef(*this)]() mutable {
cdumez@apple.comda77cf72016-05-28 05:51:42 +0000646 doNotifyFinish(protectedThis);
andersca@apple.com31366e82015-08-21 20:50:59 +0000647 });
648
jianli@chromium.org4c6124f2010-08-23 20:09:56 +0000649}
650
651} // namespace WebCore