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 #import "CString.h"
     35 #import "FileSystem.h"
     36 #import "FormData.h"
     37 #import "ResourceHandle.h"
     38 #import "ResourceHandleClient.h"
     39 #import "SchedulePair.h"
     40 #import "WebCoreSystemInterface.h"
     41 #import <sys/stat.h>
     42 #import <sys/types.h>
     43 #import <wtf/Assertions.h>
     44 #import <wtf/HashMap.h>
     45 #import <wtf/MainThread.h>
     46 #import <wtf/StdLibExtras.h>
     47 #import <wtf/Threading.h>
     48 
     49 namespace WebCore {
     50 
     51 typedef HashMap<CFReadStreamRef, RefPtr<FormData> > StreamFormDataMap;
     52 static StreamFormDataMap& getStreamFormDataMap()
     53 {
     54     DEFINE_STATIC_LOCAL(StreamFormDataMap, streamFormDataMap, ());
     55     return streamFormDataMap;
     56 }
     57 
     58 typedef HashMap<CFReadStreamRef, RefPtr<ResourceHandle> > StreamResourceHandleMap;
     59 static StreamResourceHandleMap& getStreamResourceHandleMap()
     60 {
     61     DEFINE_STATIC_LOCAL(StreamResourceHandleMap, streamResourceHandleMap, ());
     62     return streamResourceHandleMap;
     63 }
     64 
     65 void associateStreamWithResourceHandle(NSInputStream *stream, ResourceHandle* resourceHandle)
     66 {
     67     ASSERT(isMainThread());
     68 
     69     ASSERT(resourceHandle);
     70 
     71     if (!stream)
     72         return;
     73 
     74     if (!getStreamFormDataMap().contains((CFReadStreamRef)stream))
     75         return;
     76 
     77     ASSERT(!getStreamResourceHandleMap().contains((CFReadStreamRef)stream));
     78     getStreamResourceHandleMap().set((CFReadStreamRef)stream, resourceHandle);
     79 }
     80 
     81 void disassociateStreamWithResourceHandle(NSInputStream *stream)
     82 {
     83     ASSERT(isMainThread());
     84 
     85     if (!stream)
     86         return;
     87 
     88     getStreamResourceHandleMap().remove((CFReadStreamRef)stream);
     89 }
     90 
     91 struct DidSendDataCallbackData {
     92     DidSendDataCallbackData(CFReadStreamRef stream_, unsigned long long bytesSent_, unsigned long long streamLength_)
     93         : stream(stream_)
     94         , bytesSent(bytesSent_)
     95         , streamLength(streamLength_)
     96     {
     97     }
     98 
     99     CFReadStreamRef stream;
    100     unsigned long long bytesSent;
    101     unsigned long long streamLength;
    102 };
    103 
    104 static void performDidSendDataCallback(void* context)
    105 {
    106     ASSERT(isMainThread());
    107 
    108     DidSendDataCallbackData* data = static_cast<DidSendDataCallbackData*>(context);
    109     ResourceHandle* resourceHandle = getStreamResourceHandleMap().get(data->stream).get();
    110 
    111     if (resourceHandle && resourceHandle->client())
    112         resourceHandle->client()->didSendData(resourceHandle, data->bytesSent, data->streamLength);
    113 
    114     delete data;
    115 }
    116 
    117 static void formEventCallback(CFReadStreamRef stream, CFStreamEventType type, void* context);
    118 
    119 struct FormContext {
    120     FormData* formData;
    121     unsigned long long streamLength;
    122 };
    123 
    124 struct FormStreamFields {
    125     SchedulePairHashSet scheduledRunLoopPairs;
    126     Vector<FormDataElement> remainingElements; // in reverse order
    127     CFReadStreamRef currentStream;
    128     char* currentData;
    129     CFReadStreamRef formStream;
    130     unsigned long long streamLength;
    131     unsigned long long bytesSent;
    132 };
    133 
    134 static void closeCurrentStream(FormStreamFields *form)
    135 {
    136     if (form->currentStream) {
    137         CFReadStreamClose(form->currentStream);
    138         CFReadStreamSetClient(form->currentStream, kCFStreamEventNone, NULL, NULL);
    139         CFRelease(form->currentStream);
    140         form->currentStream = NULL;
    141     }
    142     if (form->currentData) {
    143         fastFree(form->currentData);
    144         form->currentData = 0;
    145     }
    146 }
    147 
    148 static void advanceCurrentStream(FormStreamFields *form)
    149 {
    150     closeCurrentStream(form);
    151 
    152     if (form->remainingElements.isEmpty())
    153         return;
    154 
    155     // Create the new stream.
    156     FormDataElement& nextInput = form->remainingElements.last();
    157     if (nextInput.m_type == FormDataElement::data) {
    158         size_t size = nextInput.m_data.size();
    159         char* data = nextInput.m_data.releaseBuffer();
    160         form->currentStream = CFReadStreamCreateWithBytesNoCopy(0, reinterpret_cast<const UInt8*>(data), size, kCFAllocatorNull);
    161         form->currentData = data;
    162     } else {
    163         const String& path = nextInput.m_shouldGenerateFile ? nextInput.m_generatedFilename : nextInput.m_filename;
    164         RetainPtr<CFStringRef> filename(AdoptCF, path.createCFString());
    165         RetainPtr<CFURLRef> fileURL(AdoptCF, CFURLCreateWithFileSystemPath(0, filename.get(), kCFURLPOSIXPathStyle, FALSE));
    166         form->currentStream = CFReadStreamCreateWithFile(0, fileURL.get());
    167     }
    168     form->remainingElements.removeLast();
    169 
    170     // Set up the callback.
    171     CFStreamClientContext context = { 0, form, NULL, NULL, NULL };
    172     CFReadStreamSetClient(form->currentStream, kCFStreamEventHasBytesAvailable | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered,
    173         formEventCallback, &context);
    174 
    175     // Schedule with the current set of run loops.
    176     SchedulePairHashSet::iterator end = form->scheduledRunLoopPairs.end();
    177     for (SchedulePairHashSet::iterator it = form->scheduledRunLoopPairs.begin(); it != end; ++it)
    178         CFReadStreamScheduleWithRunLoop(form->currentStream, (*it)->runLoop(), (*it)->mode());
    179 }
    180 
    181 static void openNextStream(FormStreamFields* form)
    182 {
    183     // Skip over any streams we can't open.
    184     // For some purposes we might want to return an error, but the current NSURLConnection
    185     // can't really do anything useful with an error at this point, so this is better.
    186     advanceCurrentStream(form);
    187     while (form->currentStream && !CFReadStreamOpen(form->currentStream))
    188         advanceCurrentStream(form);
    189 }
    190 
    191 static void* formCreate(CFReadStreamRef stream, void* context)
    192 {
    193     FormContext* formContext = static_cast<FormContext*>(context);
    194 
    195     FormStreamFields* newInfo = new FormStreamFields;
    196     newInfo->currentStream = NULL;
    197     newInfo->currentData = 0;
    198     newInfo->formStream = stream; // Don't retain. That would create a reference cycle.
    199     newInfo->streamLength = formContext->streamLength;
    200     newInfo->bytesSent = 0;
    201 
    202     FormData* formData = formContext->formData;
    203 
    204     // Append in reverse order since we remove elements from the end.
    205     size_t size = formData->elements().size();
    206     newInfo->remainingElements.reserveInitialCapacity(size);
    207     for (size_t i = 0; i < size; ++i)
    208         newInfo->remainingElements.append(formData->elements()[size - i - 1]);
    209 
    210     getStreamFormDataMap().set(stream, adoptRef(formData));
    211 
    212     return newInfo;
    213 }
    214 
    215 static void formFinalize(CFReadStreamRef stream, void* context)
    216 {
    217     FormStreamFields* form = static_cast<FormStreamFields*>(context);
    218 
    219     getStreamFormDataMap().remove(stream);
    220 
    221     closeCurrentStream(form);
    222     delete form;
    223 }
    224 
    225 static Boolean formOpen(CFReadStreamRef, CFStreamError* error, Boolean* openComplete, void* context)
    226 {
    227     FormStreamFields* form = static_cast<FormStreamFields*>(context);
    228 
    229     openNextStream(form);
    230 
    231     *openComplete = TRUE;
    232     error->error = 0;
    233     return TRUE;
    234 }
    235 
    236 static CFIndex formRead(CFReadStreamRef stream, UInt8* buffer, CFIndex bufferLength, CFStreamError* error, Boolean* atEOF, void* context)
    237 {
    238     FormStreamFields* form = static_cast<FormStreamFields*>(context);
    239 
    240     while (form->currentStream) {
    241         CFIndex bytesRead = CFReadStreamRead(form->currentStream, buffer, bufferLength);
    242         if (bytesRead < 0) {
    243             *error = CFReadStreamGetError(form->currentStream);
    244             return -1;
    245         }
    246         if (bytesRead > 0) {
    247             error->error = 0;
    248             *atEOF = FALSE;
    249             form->bytesSent += bytesRead;
    250 
    251             if (!ResourceHandle::didSendBodyDataDelegateExists()) {
    252                 // FIXME: Figure out how to only do this when a ResourceHandleClient is available.
    253                 DidSendDataCallbackData* data = new DidSendDataCallbackData(stream, form->bytesSent, form->streamLength);
    254                 callOnMainThread(performDidSendDataCallback, data);
    255             }
    256 
    257             return bytesRead;
    258         }
    259         openNextStream(form);
    260     }
    261 
    262     error->error = 0;
    263     *atEOF = TRUE;
    264     return 0;
    265 }
    266 
    267 static Boolean formCanRead(CFReadStreamRef stream, void* context)
    268 {
    269     FormStreamFields* form = static_cast<FormStreamFields*>(context);
    270 
    271     while (form->currentStream && CFReadStreamGetStatus(form->currentStream) == kCFStreamStatusAtEnd) {
    272         openNextStream(form);
    273     }
    274     if (!form->currentStream) {
    275         wkSignalCFReadStreamEnd(stream);
    276         return FALSE;
    277     }
    278     return CFReadStreamHasBytesAvailable(form->currentStream);
    279 }
    280 
    281 static void formClose(CFReadStreamRef, void* context)
    282 {
    283     FormStreamFields* form = static_cast<FormStreamFields*>(context);
    284 
    285     closeCurrentStream(form);
    286 }
    287 
    288 static void formSchedule(CFReadStreamRef, CFRunLoopRef runLoop, CFStringRef runLoopMode, void* context)
    289 {
    290     FormStreamFields* form = static_cast<FormStreamFields*>(context);
    291 
    292     if (form->currentStream)
    293         CFReadStreamScheduleWithRunLoop(form->currentStream, runLoop, runLoopMode);
    294     form->scheduledRunLoopPairs.add(SchedulePair::create(runLoop, runLoopMode));
    295 }
    296 
    297 static void formUnschedule(CFReadStreamRef, CFRunLoopRef runLoop, CFStringRef runLoopMode, void* context)
    298 {
    299     FormStreamFields* form = static_cast<FormStreamFields*>(context);
    300 
    301     if (form->currentStream)
    302         CFReadStreamUnscheduleFromRunLoop(form->currentStream, runLoop, runLoopMode);
    303     form->scheduledRunLoopPairs.remove(SchedulePair::create(runLoop, runLoopMode));
    304 }
    305 
    306 static void formEventCallback(CFReadStreamRef stream, CFStreamEventType type, void* context)
    307 {
    308     FormStreamFields* form = static_cast<FormStreamFields*>(context);
    309 
    310     switch (type) {
    311     case kCFStreamEventHasBytesAvailable:
    312         wkSignalCFReadStreamHasBytes(form->formStream);
    313         break;
    314     case kCFStreamEventErrorOccurred: {
    315         CFStreamError readStreamError = CFReadStreamGetError(stream);
    316         wkSignalCFReadStreamError(form->formStream, &readStreamError);
    317         break;
    318     }
    319     case kCFStreamEventEndEncountered:
    320         openNextStream(form);
    321         if (!form->currentStream) {
    322             wkSignalCFReadStreamEnd(form->formStream);
    323         }
    324         break;
    325     case kCFStreamEventNone:
    326         LOG_ERROR("unexpected kCFStreamEventNone");
    327         break;
    328     case kCFStreamEventOpenCompleted:
    329         LOG_ERROR("unexpected kCFStreamEventOpenCompleted");
    330         break;
    331     case kCFStreamEventCanAcceptBytes:
    332         LOG_ERROR("unexpected kCFStreamEventCanAcceptBytes");
    333         break;
    334     }
    335 }
    336 
    337 void setHTTPBody(NSMutableURLRequest *request, PassRefPtr<FormData> formData)
    338 {
    339     if (!formData)
    340         return;
    341 
    342     size_t count = formData->elements().size();
    343 
    344     // Handle the common special case of one piece of form data, with no files.
    345     if (count == 1 && !formData->alwaysStream()) {
    346         const FormDataElement& element = formData->elements()[0];
    347         if (element.m_type == FormDataElement::data) {
    348             NSData *data = [[NSData alloc] initWithBytes:element.m_data.data() length:element.m_data.size()];
    349             [request setHTTPBody:data];
    350             [data release];
    351             return;
    352         }
    353     }
    354 
    355     // Precompute the content length so NSURLConnection doesn't use chunked mode.
    356     long long length = 0;
    357     for (size_t i = 0; i < count; ++i) {
    358         const FormDataElement& element = formData->elements()[i];
    359         if (element.m_type == FormDataElement::data)
    360             length += element.m_data.size();
    361         else {
    362             long long fileSize;
    363             if (getFileSize(element.m_shouldGenerateFile ? element.m_generatedFilename : element.m_filename, fileSize))
    364                 length += fileSize;
    365         }
    366     }
    367 
    368     // Set the length.
    369     [request setValue:[NSString stringWithFormat:@"%lld", length] forHTTPHeaderField:@"Content-Length"];
    370 
    371     // Create and set the stream.
    372 
    373     // Pass the length along with the formData so it does not have to be recomputed.
    374     FormContext formContext = { formData.releaseRef(), length };
    375 
    376     RetainPtr<CFReadStreamRef> stream(AdoptCF, wkCreateCustomCFReadStream(formCreate, formFinalize,
    377         formOpen, formRead, formCanRead, formClose, formSchedule, formUnschedule,
    378         &formContext));
    379     [request setHTTPBodyStream:(NSInputStream *)stream.get()];
    380 }
    381 
    382 FormData* httpBodyFromStream(NSInputStream* stream)
    383 {
    384     return getStreamFormDataMap().get((CFReadStreamRef)stream).get();
    385 }
    386 
    387 } // namespace WebCore
    388