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