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