Home | History | Annotate | Download | only in mac
      1 /*
      2  * Copyright (C) 2005, 2006, 2008 Apple 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
      6  * are met:
      7  *
      8  * 1.  Redistributions of source code must retain the above copyright
      9  *     notice, this list of conditions and the following disclaimer.
     10  * 2.  Redistributions in binary form must reproduce the above copyright
     11  *     notice, this list of conditions and the following disclaimer in the
     12  *     documentation and/or other materials provided with the distribution.
     13  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
     14  *     its contributors may be used to endorse or promote products derived
     15  *     from this software without specific prior written permission.
     16  *
     17  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
     18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     20  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
     21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
     24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     27  */
     28 
     29 /* originally written by Becky Willrich, additional code by Darin Adler */
     30 
     31 #import "config.h"
     32 #import "FormDataStreamMac.h"
     33 
     34 #if !USE(CFNETWORK)
     35 
     36 #import "BlobRegistryImpl.h"
     37 #import "FileSystem.h"
     38 #import "FormData.h"
     39 #import "ResourceHandle.h"
     40 #import "ResourceHandleClient.h"
     41 #import "SchedulePair.h"
     42 #import "WebCoreSystemInterface.h"
     43 #import <sys/stat.h>
     44 #import <sys/types.h>
     45 #import <wtf/Assertions.h>
     46 #import <wtf/HashMap.h>
     47 #import <wtf/MainThread.h>
     48 #import <wtf/StdLibExtras.h>
     49 #import <wtf/Threading.h>
     50 
     51 #if PLATFORM(IOS)
     52 #import <MacErrors.h>
     53 #else
     54 #import <CoreServices/CoreServices.h>
     55 #endif
     56 
     57 namespace WebCore {
     58 
     59 typedef HashMap<CFReadStreamRef, RefPtr<FormData> > StreamFormDataMap;
     60 static StreamFormDataMap& getStreamFormDataMap()
     61 {
     62     DEFINE_STATIC_LOCAL(StreamFormDataMap, streamFormDataMap, ());
     63     return streamFormDataMap;
     64 }
     65 
     66 typedef HashMap<CFReadStreamRef, RefPtr<ResourceHandle> > StreamResourceHandleMap;
     67 static StreamResourceHandleMap& getStreamResourceHandleMap()
     68 {
     69     DEFINE_STATIC_LOCAL(StreamResourceHandleMap, streamResourceHandleMap, ());
     70     return streamResourceHandleMap;
     71 }
     72 
     73 void associateStreamWithResourceHandle(NSInputStream *stream, ResourceHandle* resourceHandle)
     74 {
     75     ASSERT(isMainThread());
     76 
     77     ASSERT(resourceHandle);
     78 
     79     if (!stream)
     80         return;
     81 
     82     if (!getStreamFormDataMap().contains((CFReadStreamRef)stream))
     83         return;
     84 
     85     ASSERT(!getStreamResourceHandleMap().contains((CFReadStreamRef)stream));
     86     getStreamResourceHandleMap().set((CFReadStreamRef)stream, resourceHandle);
     87 }
     88 
     89 void disassociateStreamWithResourceHandle(NSInputStream *stream)
     90 {
     91     ASSERT(isMainThread());
     92 
     93     if (!stream)
     94         return;
     95 
     96     getStreamResourceHandleMap().remove((CFReadStreamRef)stream);
     97 }
     98 
     99 struct DidSendDataCallbackData {
    100     DidSendDataCallbackData(CFReadStreamRef stream_, unsigned long long bytesSent_, unsigned long long streamLength_)
    101         : stream(stream_)
    102         , bytesSent(bytesSent_)
    103         , streamLength(streamLength_)
    104     {
    105     }
    106 
    107     CFReadStreamRef stream;
    108     unsigned long long bytesSent;
    109     unsigned long long streamLength;
    110 };
    111 
    112 static void performDidSendDataCallback(void* context)
    113 {
    114     ASSERT(isMainThread());
    115 
    116     DidSendDataCallbackData* data = static_cast<DidSendDataCallbackData*>(context);
    117     ResourceHandle* resourceHandle = getStreamResourceHandleMap().get(data->stream).get();
    118 
    119     if (resourceHandle && resourceHandle->client())
    120         resourceHandle->client()->didSendData(resourceHandle, data->bytesSent, data->streamLength);
    121 
    122     delete data;
    123 }
    124 
    125 static void formEventCallback(CFReadStreamRef stream, CFStreamEventType type, void* context);
    126 
    127 struct FormContext {
    128     FormData* formData;
    129     unsigned long long streamLength;
    130 };
    131 
    132 struct FormStreamFields {
    133     SchedulePairHashSet scheduledRunLoopPairs;
    134     Vector<FormDataElement> remainingElements; // in reverse order
    135     CFReadStreamRef currentStream;
    136 #if ENABLE(BLOB)
    137     long long currentStreamRangeLength;
    138 #endif
    139     char* currentData;
    140     CFReadStreamRef formStream;
    141     unsigned long long streamLength;
    142     unsigned long long bytesSent;
    143 };
    144 
    145 static void closeCurrentStream(FormStreamFields *form)
    146 {
    147     if (form->currentStream) {
    148         CFReadStreamClose(form->currentStream);
    149         CFReadStreamSetClient(form->currentStream, kCFStreamEventNone, NULL, NULL);
    150         CFRelease(form->currentStream);
    151         form->currentStream = NULL;
    152 #if ENABLE(BLOB)
    153         form->currentStreamRangeLength = BlobDataItem::toEndOfFile;
    154 #endif
    155     }
    156     if (form->currentData) {
    157         fastFree(form->currentData);
    158         form->currentData = 0;
    159     }
    160 }
    161 
    162 // Return false if we cannot advance the stream. Currently the only possible failure is that the underlying file has been removed or changed since File.slice.
    163 static bool advanceCurrentStream(FormStreamFields* form)
    164 {
    165     closeCurrentStream(form);
    166 
    167     if (form->remainingElements.isEmpty())
    168         return true;
    169 
    170     // Create the new stream.
    171     FormDataElement& nextInput = form->remainingElements.last();
    172 
    173     if (nextInput.m_type == FormDataElement::data) {
    174         size_t size = nextInput.m_data.size();
    175         char* data = nextInput.m_data.releaseBuffer();
    176         form->currentStream = CFReadStreamCreateWithBytesNoCopy(0, reinterpret_cast<const UInt8*>(data), size, kCFAllocatorNull);
    177         form->currentData = data;
    178     } else {
    179 #if ENABLE(BLOB)
    180         // Check if the file has been changed or not if required.
    181         if (nextInput.m_expectedFileModificationTime != BlobDataItem::doNotCheckFileChange) {
    182             time_t fileModificationTime;
    183             if (!getFileModificationTime(nextInput.m_filename, fileModificationTime) || fileModificationTime != static_cast<time_t>(nextInput.m_expectedFileModificationTime))
    184                 return false;
    185         }
    186 #endif
    187         const String& path = nextInput.m_shouldGenerateFile ? nextInput.m_generatedFilename : nextInput.m_filename;
    188         form->currentStream = CFReadStreamCreateWithFile(0, pathAsURL(path).get());
    189         if (!form->currentStream) {
    190             // The file must have been removed or become unreadable.
    191             return false;
    192         }
    193 #if ENABLE(BLOB)
    194         if (nextInput.m_fileStart > 0) {
    195             RetainPtr<CFNumberRef> position(AdoptCF, CFNumberCreate(0, kCFNumberLongLongType, &nextInput.m_fileStart));
    196             CFReadStreamSetProperty(form->currentStream, kCFStreamPropertyFileCurrentOffset, position.get());
    197         }
    198         form->currentStreamRangeLength = nextInput.m_fileLength;
    199 #endif
    200     }
    201     form->remainingElements.removeLast();
    202 
    203     // Set up the callback.
    204     CFStreamClientContext context = { 0, form, NULL, NULL, NULL };
    205     CFReadStreamSetClient(form->currentStream, kCFStreamEventHasBytesAvailable | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered,
    206         formEventCallback, &context);
    207 
    208     // Schedule with the current set of run loops.
    209     SchedulePairHashSet::iterator end = form->scheduledRunLoopPairs.end();
    210     for (SchedulePairHashSet::iterator it = form->scheduledRunLoopPairs.begin(); it != end; ++it)
    211         CFReadStreamScheduleWithRunLoop(form->currentStream, (*it)->runLoop(), (*it)->mode());
    212 
    213     return true;
    214 }
    215 
    216 static bool openNextStream(FormStreamFields* form)
    217 {
    218     // Skip over any streams we can't open.
    219     if (!advanceCurrentStream(form))
    220         return false;
    221     while (form->currentStream && !CFReadStreamOpen(form->currentStream)) {
    222         if (!advanceCurrentStream(form))
    223             return false;
    224     }
    225     return true;
    226 }
    227 
    228 static void* formCreate(CFReadStreamRef stream, void* context)
    229 {
    230     FormContext* formContext = static_cast<FormContext*>(context);
    231 
    232     FormStreamFields* newInfo = new FormStreamFields;
    233     newInfo->currentStream = NULL;
    234 #if ENABLE(BLOB)
    235     newInfo->currentStreamRangeLength = BlobDataItem::toEndOfFile;
    236 #endif
    237     newInfo->currentData = 0;
    238     newInfo->formStream = stream; // Don't retain. That would create a reference cycle.
    239     newInfo->streamLength = formContext->streamLength;
    240     newInfo->bytesSent = 0;
    241 
    242     FormData* formData = formContext->formData;
    243 
    244     // Append in reverse order since we remove elements from the end.
    245     size_t size = formData->elements().size();
    246     newInfo->remainingElements.reserveInitialCapacity(size);
    247     for (size_t i = 0; i < size; ++i)
    248         newInfo->remainingElements.append(formData->elements()[size - i - 1]);
    249 
    250     getStreamFormDataMap().set(stream, adoptRef(formData));
    251 
    252     return newInfo;
    253 }
    254 
    255 static void formFinalize(CFReadStreamRef stream, void* context)
    256 {
    257     FormStreamFields* form = static_cast<FormStreamFields*>(context);
    258 
    259     getStreamFormDataMap().remove(stream);
    260 
    261     closeCurrentStream(form);
    262     delete form;
    263 }
    264 
    265 static Boolean formOpen(CFReadStreamRef, CFStreamError* error, Boolean* openComplete, void* context)
    266 {
    267     FormStreamFields* form = static_cast<FormStreamFields*>(context);
    268 
    269     bool opened = openNextStream(form);
    270 
    271     *openComplete = opened;
    272     error->error = opened ? 0 : fnfErr;
    273     return opened;
    274 }
    275 
    276 static CFIndex formRead(CFReadStreamRef stream, UInt8* buffer, CFIndex bufferLength, CFStreamError* error, Boolean* atEOF, void* context)
    277 {
    278     FormStreamFields* form = static_cast<FormStreamFields*>(context);
    279 
    280     while (form->currentStream) {
    281         CFIndex bytesToRead = bufferLength;
    282 #if ENABLE(BLOB)
    283         if (form->currentStreamRangeLength != BlobDataItem::toEndOfFile && form->currentStreamRangeLength < bytesToRead)
    284             bytesToRead = static_cast<CFIndex>(form->currentStreamRangeLength);
    285 #endif
    286         CFIndex bytesRead = CFReadStreamRead(form->currentStream, buffer, bytesToRead);
    287         if (bytesRead < 0) {
    288             *error = CFReadStreamGetError(form->currentStream);
    289             return -1;
    290         }
    291         if (bytesRead > 0) {
    292             error->error = 0;
    293             *atEOF = FALSE;
    294             form->bytesSent += bytesRead;
    295 #if ENABLE(BLOB)
    296             if (form->currentStreamRangeLength != BlobDataItem::toEndOfFile)
    297                 form->currentStreamRangeLength -= bytesRead;
    298 #endif
    299 
    300             if (!ResourceHandle::didSendBodyDataDelegateExists()) {
    301                 // FIXME: Figure out how to only do this when a ResourceHandleClient is available.
    302                 DidSendDataCallbackData* data = new DidSendDataCallbackData(stream, form->bytesSent, form->streamLength);
    303                 callOnMainThread(performDidSendDataCallback, data);
    304             }
    305 
    306             return bytesRead;
    307         }
    308         openNextStream(form);
    309     }
    310 
    311     error->error = 0;
    312     *atEOF = TRUE;
    313     return 0;
    314 }
    315 
    316 static Boolean formCanRead(CFReadStreamRef stream, void* context)
    317 {
    318     FormStreamFields* form = static_cast<FormStreamFields*>(context);
    319 
    320     while (form->currentStream && CFReadStreamGetStatus(form->currentStream) == kCFStreamStatusAtEnd) {
    321         openNextStream(form);
    322     }
    323     if (!form->currentStream) {
    324         wkSignalCFReadStreamEnd(stream);
    325         return FALSE;
    326     }
    327     return CFReadStreamHasBytesAvailable(form->currentStream);
    328 }
    329 
    330 static void formClose(CFReadStreamRef, void* context)
    331 {
    332     FormStreamFields* form = static_cast<FormStreamFields*>(context);
    333 
    334     closeCurrentStream(form);
    335 }
    336 
    337 static void formSchedule(CFReadStreamRef, CFRunLoopRef runLoop, CFStringRef runLoopMode, void* context)
    338 {
    339     FormStreamFields* form = static_cast<FormStreamFields*>(context);
    340 
    341     if (form->currentStream)
    342         CFReadStreamScheduleWithRunLoop(form->currentStream, runLoop, runLoopMode);
    343     form->scheduledRunLoopPairs.add(SchedulePair::create(runLoop, runLoopMode));
    344 }
    345 
    346 static void formUnschedule(CFReadStreamRef, CFRunLoopRef runLoop, CFStringRef runLoopMode, void* context)
    347 {
    348     FormStreamFields* form = static_cast<FormStreamFields*>(context);
    349 
    350     if (form->currentStream)
    351         CFReadStreamUnscheduleFromRunLoop(form->currentStream, runLoop, runLoopMode);
    352     form->scheduledRunLoopPairs.remove(SchedulePair::create(runLoop, runLoopMode));
    353 }
    354 
    355 static void formEventCallback(CFReadStreamRef stream, CFStreamEventType type, void* context)
    356 {
    357     FormStreamFields* form = static_cast<FormStreamFields*>(context);
    358 
    359     switch (type) {
    360     case kCFStreamEventHasBytesAvailable:
    361         wkSignalCFReadStreamHasBytes(form->formStream);
    362         break;
    363     case kCFStreamEventErrorOccurred: {
    364         CFStreamError readStreamError = CFReadStreamGetError(stream);
    365         wkSignalCFReadStreamError(form->formStream, &readStreamError);
    366         break;
    367     }
    368     case kCFStreamEventEndEncountered:
    369         openNextStream(form);
    370         if (!form->currentStream) {
    371             wkSignalCFReadStreamEnd(form->formStream);
    372         }
    373         break;
    374     case kCFStreamEventNone:
    375         LOG_ERROR("unexpected kCFStreamEventNone");
    376         break;
    377     case kCFStreamEventOpenCompleted:
    378         LOG_ERROR("unexpected kCFStreamEventOpenCompleted");
    379         break;
    380     case kCFStreamEventCanAcceptBytes:
    381         LOG_ERROR("unexpected kCFStreamEventCanAcceptBytes");
    382         break;
    383     }
    384 }
    385 
    386 void setHTTPBody(NSMutableURLRequest *request, PassRefPtr<FormData> formData)
    387 {
    388     if (!formData)
    389         return;
    390 
    391     size_t count = formData->elements().size();
    392 
    393     // Handle the common special case of one piece of form data, with no files.
    394     if (count == 1 && !formData->alwaysStream()) {
    395         const FormDataElement& element = formData->elements()[0];
    396         if (element.m_type == FormDataElement::data) {
    397             NSData *data = [[NSData alloc] initWithBytes:element.m_data.data() length:element.m_data.size()];
    398             [request setHTTPBody:data];
    399             [data release];
    400             return;
    401         }
    402     }
    403 
    404 #if ENABLE(BLOB)
    405     // Check if there is a blob in the form data.
    406     bool hasBlob = false;
    407     for (size_t i = 0; i < count; ++i) {
    408         const FormDataElement& element = formData->elements()[i];
    409         if (element.m_type == FormDataElement::encodedBlob) {
    410             hasBlob = true;
    411             break;
    412         }
    413     }
    414 
    415     // If yes, we have to resolve all the blob references and regenerate the form data with only data and file types.
    416     if (hasBlob) {
    417         RefPtr<FormData> newFormData = FormData::create();
    418         newFormData->setAlwaysStream(formData->alwaysStream());
    419         newFormData->setIdentifier(formData->identifier());
    420         for (size_t i = 0; i < count; ++i) {
    421             const FormDataElement& element = formData->elements()[i];
    422             if (element.m_type == FormDataElement::data)
    423                 newFormData->appendData(element.m_data.data(), element.m_data.size());
    424             else if (element.m_type == FormDataElement::encodedFile)
    425                 newFormData->appendFile(element.m_filename, element.m_shouldGenerateFile);
    426             else {
    427                 ASSERT(element.m_type == FormDataElement::encodedBlob);
    428                 RefPtr<BlobStorageData> blobData = static_cast<BlobRegistryImpl&>(blobRegistry()).getBlobDataFromURL(KURL(ParsedURLString, element.m_blobURL));
    429                 if (blobData) {
    430                     for (size_t j = 0; j < blobData->items().size(); ++j) {
    431                         const BlobDataItem& blobItem = blobData->items()[j];
    432                         if (blobItem.type == BlobDataItem::Data) {
    433                             newFormData->appendData(blobItem.data->data() + static_cast<int>(blobItem.offset), static_cast<int>(blobItem.length));
    434                         } else {
    435                             ASSERT(blobItem.type == BlobDataItem::File);
    436                             newFormData->appendFileRange(blobItem.path, blobItem.offset, blobItem.length, blobItem.expectedModificationTime);
    437                         }
    438                     }
    439                 }
    440             }
    441         }
    442         formData = newFormData;
    443         count = formData->elements().size();
    444     }
    445 #endif
    446 
    447     // Precompute the content length so NSURLConnection doesn't use chunked mode.
    448     long long length = 0;
    449     for (size_t i = 0; i < count; ++i) {
    450         const FormDataElement& element = formData->elements()[i];
    451         if (element.m_type == FormDataElement::data)
    452             length += element.m_data.size();
    453         else {
    454 #if ENABLE(BLOB)
    455             // If we're sending the file range, use the existing range length for now. We will detect if the file has been changed right before we read the file and abort the operation if necessary.
    456             if (element.m_fileLength != BlobDataItem::toEndOfFile) {
    457                 length += element.m_fileLength;
    458                 continue;
    459             }
    460 #endif
    461             long long fileSize;
    462             if (getFileSize(element.m_shouldGenerateFile ? element.m_generatedFilename : element.m_filename, fileSize))
    463                 length += fileSize;
    464         }
    465     }
    466 
    467     // Set the length.
    468     [request setValue:[NSString stringWithFormat:@"%lld", length] forHTTPHeaderField:@"Content-Length"];
    469 
    470     // Create and set the stream.
    471 
    472     // Pass the length along with the formData so it does not have to be recomputed.
    473     FormContext formContext = { formData.releaseRef(), length };
    474 
    475     RetainPtr<CFReadStreamRef> stream(AdoptCF, wkCreateCustomCFReadStream(formCreate, formFinalize,
    476         formOpen, formRead, formCanRead, formClose, formSchedule, formUnschedule,
    477         &formContext));
    478     [request setHTTPBodyStream:(NSInputStream *)stream.get()];
    479 }
    480 
    481 FormData* httpBodyFromStream(NSInputStream* stream)
    482 {
    483     return getStreamFormDataMap().get((CFReadStreamRef)stream).get();
    484 }
    485 
    486 } // namespace WebCore
    487 
    488 #endif // !USE(CFNETWORK)
    489