REGRESSION: Blob URLs broken with NetworkProcess
[WebKit.git] / Source / WebCore / platform / network / BlobResourceHandle.cpp
1 /*
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
33 #if ENABLE(BLOB)
34
35 #include "BlobResourceHandle.h"
36
37 #include "AsyncFileStream.h"
38 #include "BlobStorageData.h"
39 #include "FileStream.h"
40 #include "FileSystem.h"
41 #include "HTTPParsers.h"
42 #include "KURL.h"
43 #include "ResourceError.h"
44 #include "ResourceHandleClient.h"
45 #include "ResourceRequest.h"
46 #include "ResourceResponse.h"
47 #include "SharedBuffer.h"
48 #include <wtf/MainThread.h>
49
50 namespace WebCore {
51
52 static const unsigned bufferSize = 512 * 1024;
53 static const long long positionNotSpecified = -1;
54
55 static const int httpOK = 200;
56 static const int httpPartialContent = 206;
57 static const int httpNotAllowed = 403;
58 static const int httpNotFound = 404;
59 static const int httpRequestedRangeNotSatisfiable = 416;
60 static const int httpInternalError = 500;
61 static const char* httpOKText = "OK";
62 static const char* httpPartialContentText = "Partial Content";
63 static const char* httpNotAllowedText = "Not Allowed";
64 static const char* httpNotFoundText = "Not Found";
65 static const char* httpRequestedRangeNotSatisfiableText = "Requested Range Not Satisfiable";
66 static const char* httpInternalErrorText = "Internal Server Error";
67
68 static const char* const webKitBlobResourceDomain = "WebKitBlobResource";
69 enum {
70     notFoundError = 1,
71     securityError = 2,
72     rangeError = 3,
73     notReadableError = 4,
74     methodNotAllowed = 5
75 };
76
77 ///////////////////////////////////////////////////////////////////////////////
78 // BlobResourceSynchronousLoader
79
80 namespace {
81
82 class BlobResourceSynchronousLoader : public ResourceHandleClient {
83 public:
84     BlobResourceSynchronousLoader(ResourceError&, ResourceResponse&, Vector<char>&);
85
86     virtual void didReceiveResponse(ResourceHandle*, const ResourceResponse&) OVERRIDE;
87     virtual void didReceiveData(ResourceHandle*, const char*, int, int /*encodedDataLength*/) OVERRIDE;
88     virtual void didFinishLoading(ResourceHandle*, double /*finishTime*/) OVERRIDE;
89     virtual void didFail(ResourceHandle*, const ResourceError&) OVERRIDE;
90
91 private:
92     ResourceError& m_error;
93     ResourceResponse& m_response;
94     Vector<char>& m_data;
95 };
96
97 BlobResourceSynchronousLoader::BlobResourceSynchronousLoader(ResourceError& error, ResourceResponse& response, Vector<char>& data)
98     : m_error(error)
99     , m_response(response)
100     , m_data(data)
101 {
102 }
103
104 void BlobResourceSynchronousLoader::didReceiveResponse(ResourceHandle* handle, const ResourceResponse& response)
105 {
106     // We cannot handle the size that is more than maximum integer.
107     if (response.expectedContentLength() > INT_MAX) {
108         m_error = ResourceError(webKitBlobResourceDomain, notReadableError, response.url(), "File is too large");
109         return;
110     }
111
112     m_response = response;
113
114     // Read all the data.
115     m_data.resize(static_cast<size_t>(response.expectedContentLength()));
116     static_cast<BlobResourceHandle*>(handle)->readSync(m_data.data(), static_cast<int>(m_data.size()));
117 }
118
119 void BlobResourceSynchronousLoader::didReceiveData(ResourceHandle*, const char*, int, int)
120 {
121 }
122
123 void BlobResourceSynchronousLoader::didFinishLoading(ResourceHandle*, double)
124 {
125 }
126
127 void BlobResourceSynchronousLoader::didFail(ResourceHandle*, const ResourceError& error)
128 {
129     m_error = error;
130 }
131
132 }
133
134 ///////////////////////////////////////////////////////////////////////////////
135 // BlobResourceHandle
136
137 PassRefPtr<BlobResourceHandle> BlobResourceHandle::createAsync(BlobStorageData* blobData, const ResourceRequest& request, ResourceHandleClient* client)
138 {
139     // FIXME: Should probably call didFail() instead of blocking the load without explanation.
140     if (!equalIgnoringCase(request.httpMethod(), "GET"))
141         return 0;
142
143     return adoptRef(new BlobResourceHandle(blobData, request, client, true));
144 }
145
146 void BlobResourceHandle::loadResourceSynchronously(BlobStorageData* blobData, const ResourceRequest& request, ResourceError& error, ResourceResponse& response, Vector<char>& data)
147 {
148     if (!equalIgnoringCase(request.httpMethod(), "GET")) {
149         error = ResourceError(webKitBlobResourceDomain, methodNotAllowed, response.url(), "Request method must be GET");
150         return;
151     }
152
153     BlobResourceSynchronousLoader loader(error, response, data);
154     RefPtr<BlobResourceHandle> handle = adoptRef(new BlobResourceHandle(blobData, request, &loader, false));
155     handle->start();
156 }
157
158 BlobResourceHandle::BlobResourceHandle(PassRefPtr<BlobStorageData> blobData, const ResourceRequest& request, ResourceHandleClient* client, bool async)
159     : ResourceHandle(0, request, client, false, false)
160     , m_blobData(blobData)
161     , m_async(async)
162     , m_errorCode(0)
163     , m_aborted(false)
164     , m_rangeOffset(positionNotSpecified)
165     , m_rangeEnd(positionNotSpecified)
166     , m_rangeSuffixLength(positionNotSpecified)
167     , m_totalRemainingSize(0)
168     , m_currentItemReadSize(0)
169     , m_sizeItemCount(0)
170     , m_readItemCount(0)
171     , m_fileOpened(false)
172 {
173     if (m_async)
174         m_asyncStream = AsyncFileStream::create(this);
175     else
176         m_stream = FileStream::create();
177 }
178
179 BlobResourceHandle::~BlobResourceHandle()
180 {
181     if (m_async) {
182         if (m_asyncStream)
183             m_asyncStream->stop();
184     } else {
185         if (m_stream)
186             m_stream->stop();
187     }
188 }
189
190 void BlobResourceHandle::cancel()
191 {
192     if (m_async) {
193         if (m_asyncStream) {
194             m_asyncStream->stop();
195             m_asyncStream = 0;
196         }
197     }
198
199     m_aborted = true;
200
201     ResourceHandle::cancel();
202 }
203
204 void delayedStartBlobResourceHandle(void* context)
205 {
206     RefPtr<BlobResourceHandle> handle = adoptRef(static_cast<BlobResourceHandle*>(context));
207     handle->doStart();
208 }
209
210 void BlobResourceHandle::start()
211 {
212     if (m_async) {
213         // Keep BlobResourceHandle alive until delayedStartBlobResourceHandle runs.
214         ref();
215
216         // Finish this async call quickly and return.
217         callOnMainThread(delayedStartBlobResourceHandle, this);
218         return;
219     }
220
221     doStart();
222 }
223
224 void BlobResourceHandle::doStart()
225 {
226     // Do not continue if the request is aborted or an error occurs.
227     if (m_aborted || m_errorCode)
228         return;
229
230     // If the blob data is not found, fail now.
231     if (!m_blobData) {
232         m_errorCode = notFoundError;
233         notifyResponse();
234         return;
235     }
236
237     // Parse the "Range" header we care about.
238     String range = firstRequest().httpHeaderField("Range");
239     if (!range.isEmpty() && !parseRange(range, m_rangeOffset, m_rangeEnd, m_rangeSuffixLength)) {
240         m_errorCode = rangeError;
241         notifyResponse();
242         return;
243     }
244
245     if (m_async)
246         getSizeForNext();
247     else {
248         RefPtr<BlobResourceHandle> protect(this); // getSizeForNext calls the client
249         for (size_t i = 0; i < m_blobData->items().size() && !m_aborted && !m_errorCode; ++i)
250             getSizeForNext();
251         notifyResponse();
252     }
253 }
254
255 void BlobResourceHandle::getSizeForNext()
256 {
257     // Do we finish validating and counting size for all items?
258     if (m_sizeItemCount >= m_blobData->items().size()) {
259         seek();
260
261         // Start reading if in asynchronous mode.
262         if (m_async) {
263             RefPtr<BlobResourceHandle> protect(this);
264             notifyResponse();
265             m_buffer.resize(bufferSize);
266             readAsync();
267         }
268         return;
269     }
270
271     const BlobDataItem& item = m_blobData->items().at(m_sizeItemCount);
272     switch (item.type) {
273     case BlobDataItem::Data:
274         didGetSize(item.length);
275         break;
276     case BlobDataItem::File:
277         if (m_async)
278             m_asyncStream->getSize(item.path, item.expectedModificationTime);
279         else
280             didGetSize(m_stream->getSize(item.path, item.expectedModificationTime));
281         break;
282     default:
283         ASSERT_NOT_REACHED();
284     }
285 }
286
287 void BlobResourceHandle::didGetSize(long long size)
288 {
289     // Do not continue if the request is aborted or an error occurs.
290     if (m_aborted || m_errorCode)
291         return;
292
293     // If the size is -1, it means the file has been moved or changed. Fail now.
294     if (size == -1) {
295         m_errorCode = notFoundError;
296         notifyResponse();
297         return;
298     }
299
300     // 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.
301     const BlobDataItem& item = m_blobData->items().at(m_sizeItemCount);
302     if (item.type == BlobDataItem::File && item.length != BlobDataItem::toEndOfFile)
303         size = item.length;
304
305     // Cache the size.
306     m_itemLengthList.append(size);
307
308     // Count the size.
309     m_totalRemainingSize += size;
310     m_sizeItemCount++;
311
312     // Continue with the next item.
313     getSizeForNext();
314 }
315
316 void BlobResourceHandle::seek()
317 {
318     // Convert from the suffix length to the range.
319     if (m_rangeSuffixLength != positionNotSpecified) {
320         m_rangeOffset = m_totalRemainingSize - m_rangeSuffixLength;
321         m_rangeEnd = m_rangeOffset + m_rangeSuffixLength - 1;
322     }
323
324     // Bail out if the range is not provided.
325     if (m_rangeOffset == positionNotSpecified)
326         return;
327
328     // Skip the initial items that are not in the range.
329     long long offset = m_rangeOffset;
330     for (m_readItemCount = 0; m_readItemCount < m_blobData->items().size() && offset >= m_itemLengthList[m_readItemCount]; ++m_readItemCount)
331         offset -= m_itemLengthList[m_readItemCount];
332
333     // Set the offset that need to jump to for the first item in the range.
334     m_currentItemReadSize = offset;
335
336     // Adjust the total remaining size in order not to go beyond the range.
337     if (m_rangeEnd != positionNotSpecified) {
338         long long rangeSize = m_rangeEnd - m_rangeOffset + 1;
339         if (m_totalRemainingSize > rangeSize)
340             m_totalRemainingSize = rangeSize;
341     } else
342         m_totalRemainingSize -= m_rangeOffset;
343 }
344
345 int BlobResourceHandle::readSync(char* buf, int length)
346 {
347     ASSERT(!m_async);
348     RefPtr<BlobResourceHandle> protect(this);
349
350     int offset = 0;
351     int remaining = length;
352     while (remaining) {
353         // Do not continue if the request is aborted or an error occurs.
354         if (m_aborted || m_errorCode)
355             break;
356
357         // If there is no more remaining data to read, we are done.
358         if (!m_totalRemainingSize || m_readItemCount >= m_blobData->items().size())
359             break;
360         
361         const BlobDataItem& item = m_blobData->items().at(m_readItemCount);
362         int bytesRead = 0;
363         if (item.type == BlobDataItem::Data)
364             bytesRead = readDataSync(item, buf + offset, remaining);
365         else if (item.type == BlobDataItem::File)
366             bytesRead = readFileSync(item, buf + offset, remaining);
367         else
368             ASSERT_NOT_REACHED();
369
370         if (bytesRead > 0) {
371             offset += bytesRead;
372             remaining -= bytesRead;
373         }
374     }
375
376     int result;
377     if (m_aborted || m_errorCode)
378         result = -1;
379     else
380         result = length - remaining;
381
382     if (result > 0)
383         notifyReceiveData(buf, result);
384
385     if (!result)
386         notifyFinish();
387
388     return result;
389 }
390
391 int BlobResourceHandle::readDataSync(const BlobDataItem& item, char* buf, int length)
392 {
393     ASSERT(!m_async);
394
395     long long remaining = item.length - m_currentItemReadSize;
396     int bytesToRead = (length > remaining) ? static_cast<int>(remaining) : length;
397     if (bytesToRead > m_totalRemainingSize)
398         bytesToRead = static_cast<int>(m_totalRemainingSize);
399     memcpy(buf, item.data->data() + item.offset + m_currentItemReadSize, bytesToRead);
400     m_totalRemainingSize -= bytesToRead;
401
402     m_currentItemReadSize += bytesToRead;
403     if (m_currentItemReadSize == item.length) {
404         m_readItemCount++;
405         m_currentItemReadSize = 0;
406     }
407
408     return bytesToRead;
409 }
410
411 int BlobResourceHandle::readFileSync(const BlobDataItem& item, char* buf, int length)
412 {
413     ASSERT(!m_async);
414
415     if (!m_fileOpened) {
416         long long bytesToRead = m_itemLengthList[m_readItemCount] - m_currentItemReadSize;
417         if (bytesToRead > m_totalRemainingSize)
418             bytesToRead = m_totalRemainingSize;
419         bool success = m_stream->openForRead(item.path, item.offset + m_currentItemReadSize, bytesToRead);
420         m_currentItemReadSize = 0;
421         if (!success) {
422             m_errorCode = notReadableError;
423             return 0;
424         }
425
426         m_fileOpened = true;
427     }
428
429     int bytesRead = m_stream->read(buf, length);
430     if (bytesRead < 0) {
431         m_errorCode = notReadableError;
432         return 0;
433     }
434     if (!bytesRead) {
435         m_stream->close();
436         m_fileOpened = false;
437         m_readItemCount++;
438     } else
439         m_totalRemainingSize -= bytesRead;
440
441     return bytesRead;
442 }
443
444 void BlobResourceHandle::readAsync()
445 {
446     ASSERT(m_async);
447
448     // Do not continue if the request is aborted or an error occurs.
449     if (m_aborted || m_errorCode)
450         return;
451
452     // If there is no more remaining data to read, we are done.
453     if (!m_totalRemainingSize || m_readItemCount >= m_blobData->items().size()) {
454         notifyFinish();
455         return;
456     }
457
458     const BlobDataItem& item = m_blobData->items().at(m_readItemCount);
459     if (item.type == BlobDataItem::Data)
460         readDataAsync(item);
461     else if (item.type == BlobDataItem::File)
462         readFileAsync(item);
463     else
464         ASSERT_NOT_REACHED();
465 }
466
467 void BlobResourceHandle::readDataAsync(const BlobDataItem& item)
468 {
469     ASSERT(m_async);
470     RefPtr<BlobResourceHandle> protect(this);
471
472     long long bytesToRead = item.length - m_currentItemReadSize;
473     if (bytesToRead > m_totalRemainingSize)
474         bytesToRead = m_totalRemainingSize;
475     consumeData(item.data->data() + item.offset + m_currentItemReadSize, static_cast<int>(bytesToRead));
476     m_currentItemReadSize = 0;
477 }
478
479 void BlobResourceHandle::readFileAsync(const BlobDataItem& item)
480 {
481     ASSERT(m_async);
482
483     if (m_fileOpened) {
484         m_asyncStream->read(m_buffer.data(), m_buffer.size());
485         return;
486     }
487
488     long long bytesToRead = m_itemLengthList[m_readItemCount] - m_currentItemReadSize;
489     if (bytesToRead > m_totalRemainingSize)
490         bytesToRead = static_cast<int>(m_totalRemainingSize);
491     m_asyncStream->openForRead(item.path, item.offset + m_currentItemReadSize, bytesToRead);
492     m_fileOpened = true;
493     m_currentItemReadSize = 0;
494 }
495
496 void BlobResourceHandle::didOpen(bool success)
497 {
498     ASSERT(m_async);
499
500     if (!success) {
501         failed(notReadableError);
502         return;
503     }
504
505     // Continue the reading.
506     readAsync();
507 }
508
509 void BlobResourceHandle::didRead(int bytesRead)
510 {
511     if (bytesRead < 0) {
512         failed(notReadableError);
513         return;
514     }
515
516     consumeData(m_buffer.data(), bytesRead);
517 }
518
519 void BlobResourceHandle::consumeData(const char* data, int bytesRead)
520 {
521     ASSERT(m_async);
522     RefPtr<BlobResourceHandle> protect(this);
523
524     m_totalRemainingSize -= bytesRead;
525
526     // Notify the client.
527     if (bytesRead)
528         notifyReceiveData(data, bytesRead);
529
530     if (m_fileOpened) {
531         // When the current item is a file item, the reading is completed only if bytesRead is 0.
532         if (!bytesRead) {
533             // Close the file.
534             m_fileOpened = false;
535             m_asyncStream->close();
536
537             // Move to the next item.
538             m_readItemCount++;
539         }
540     } else {
541         // Otherwise, we read the current text item as a whole and move to the next item.
542         m_readItemCount++;
543     }
544
545     // Continue the reading.
546     readAsync();
547 }
548
549 void BlobResourceHandle::failed(int errorCode)
550 {
551     ASSERT(m_async);
552     RefPtr<BlobResourceHandle> protect(this);
553
554     // Notify the client.
555     notifyFail(errorCode);
556
557     // Close the file if needed.
558     if (m_fileOpened) {
559         m_fileOpened = false;
560         m_asyncStream->close();
561     }
562 }
563
564 void BlobResourceHandle::notifyResponse()
565 {
566     if (!client())
567         return;
568
569     if (m_errorCode) {
570         RefPtr<BlobResourceHandle> protect(this);
571         notifyResponseOnError();
572         notifyFinish();
573     } else
574         notifyResponseOnSuccess();
575 }
576
577 void BlobResourceHandle::notifyResponseOnSuccess()
578 {
579     bool isRangeRequest = m_rangeOffset != positionNotSpecified;
580     ResourceResponse response(firstRequest().url(), m_blobData->contentType(), m_totalRemainingSize, String(), String());
581     response.setExpectedContentLength(m_totalRemainingSize);
582     response.setHTTPStatusCode(isRangeRequest ? httpPartialContent : httpOK);
583     response.setHTTPStatusText(isRangeRequest ? httpPartialContentText : httpOKText);
584     if (!m_blobData->contentDisposition().isEmpty())
585         response.setHTTPHeaderField("Content-Disposition", m_blobData->contentDisposition());
586
587     // BlobResourceHandle cannot be used with downloading, and doesn't even wait for continueDidReceiveResponse.
588     // It's currently client's responsibility to know that didReceiveResponseAsync cannot be used to convert a
589     // load into a download or blobs.
590     if (client()->usesAsyncCallbacks())
591         client()->didReceiveResponseAsync(this, response);
592     else
593         client()->didReceiveResponse(this, response);
594 }
595
596 void BlobResourceHandle::notifyResponseOnError()
597 {
598     ASSERT(m_errorCode);
599
600     ResourceResponse response(firstRequest().url(), "text/plain", 0, String(), String());
601     switch (m_errorCode) {
602     case rangeError:
603         response.setHTTPStatusCode(httpRequestedRangeNotSatisfiable);
604         response.setHTTPStatusText(httpRequestedRangeNotSatisfiableText);
605         break;
606     case notFoundError:
607         response.setHTTPStatusCode(httpNotFound);
608         response.setHTTPStatusText(httpNotFoundText);
609         break;
610     case securityError:
611         response.setHTTPStatusCode(httpNotAllowed);
612         response.setHTTPStatusText(httpNotAllowedText);
613         break;
614     default:
615         response.setHTTPStatusCode(httpInternalError);
616         response.setHTTPStatusText(httpInternalErrorText);
617         break;
618     }
619
620     // Note that we don't wait for continueDidReceiveResponse when using didReceiveResponseAsync.
621     // This is not formally correct, but the client has to be a no-op anyway, because blobs can't be downloaded.
622     if (client()->usesAsyncCallbacks())
623         client()->didReceiveResponseAsync(this, response);
624     else
625         client()->didReceiveResponse(this, response);
626 }
627
628 void BlobResourceHandle::notifyReceiveData(const char* data, int bytesRead)
629 {
630     if (client())
631         client()->didReceiveBuffer(this, SharedBuffer::create(data, bytesRead), bytesRead);
632 }
633
634 void BlobResourceHandle::notifyFail(int errorCode)
635 {
636     if (client())
637         client()->didFail(this, ResourceError(webKitBlobResourceDomain, errorCode, firstRequest().url(), String()));
638 }
639
640 static void doNotifyFinish(void* context)
641 {
642     BlobResourceHandle* handle = static_cast<BlobResourceHandle*>(context);
643     if (!handle->aborted() && handle->client())
644         handle->client()->didFinishLoading(handle, 0);
645
646     // Balance the ref() in BlobResourceHandle::notfyFinish().
647     handle->deref();
648 }
649
650 void BlobResourceHandle::notifyFinish()
651 {
652     // Balanced in doNotifyFinish().
653     ref();
654
655     if (m_async) {
656         // Schedule to notify the client from a standalone function because the client might dispose the handle immediately from the callback function
657         // while we still have BlobResourceHandle calls in the stack.
658         callOnMainThread(doNotifyFinish, this);
659         return;
660     }
661
662     doNotifyFinish(this);
663 }
664
665 } // namespace WebCore
666
667 #endif // ENABLE(BLOB)