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