2 * Copyright (C) 2005, 2006, 2007 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
29 /* originally written by Becky Willrich, additional code by Darin Adler */
32 #include "FormDataStreamCFNet.h"
35 #include "FileSystem.h"
37 #include <CFNetwork/CFURLRequestPriv.h>
38 #include <CoreFoundation/CFStreamAbstract.h>
39 #include <WebKitSystemInterface/WebKitSystemInterface.h>
40 #include <sys/types.h>
41 #include <wtf/Assertions.h>
42 #include <wtf/HashMap.h>
43 #include <wtf/RetainPtr.h>
45 #define USE_V1_CFSTREAM_CALLBACKS
46 #ifdef USE_V1_CFSTREAM_CALLBACKS
47 typedef CFReadStreamCallBacksV1 WCReadStreamCallBacks
;
49 typedef CFReadStreamCallBacks WCReadStreamCallBacks
;
54 static HashMap
<CFReadStreamRef
, RefPtr
<FormData
> >& getStreamFormDatas()
56 static HashMap
<CFReadStreamRef
, RefPtr
<FormData
> > streamFormDatas
;
57 return streamFormDatas
;
60 static void formEventCallback(CFReadStreamRef stream
, CFStreamEventType type
, void* context
);
62 struct FormStreamFields
{
63 CFMutableSetRef scheduledRunLoopPairs
;
64 Vector
<FormDataElement
> remainingElements
; // in reverse order
65 CFReadStreamRef currentStream
;
67 CFReadStreamRef formStream
;
75 static const void* pairRetain(CFAllocatorRef alloc
, const void* value
)
77 const SchedulePair
* pair
= static_cast<const SchedulePair
*>(value
);
79 SchedulePair
* result
= new SchedulePair
;
80 CFRetain(pair
->runLoop
);
81 result
->runLoop
= pair
->runLoop
;
82 result
->mode
= CFStringCreateCopy(alloc
, pair
->mode
);
86 static void pairRelease(CFAllocatorRef alloc
, const void* value
)
88 const SchedulePair
* pair
= static_cast<const SchedulePair
*>(value
);
90 CFRelease(pair
->runLoop
);
91 CFRelease(pair
->mode
);
95 static Boolean
pairEqual(const void* a
, const void* b
)
97 const SchedulePair
* pairA
= static_cast<const SchedulePair
*>(a
);
98 const SchedulePair
* pairB
= static_cast<const SchedulePair
*>(b
);
100 return pairA
->runLoop
== pairB
->runLoop
&& CFEqual(pairA
->mode
, pairB
->mode
);
103 static CFHashCode
pairHash(const void* value
)
105 const SchedulePair
* pair
= static_cast<const SchedulePair
*>(value
);
107 return (CFHashCode
)pair
->runLoop
^ CFHash(pair
->mode
);
110 static void closeCurrentStream(FormStreamFields
*form
)
112 if (form
->currentStream
) {
113 CFReadStreamClose(form
->currentStream
);
114 CFReadStreamSetClient(form
->currentStream
, kCFStreamEventNone
, NULL
, NULL
);
115 CFRelease(form
->currentStream
);
116 form
->currentStream
= NULL
;
118 if (form
->currentData
) {
119 fastFree(form
->currentData
);
120 form
->currentData
= 0;
124 static void scheduleWithPair(const void* value
, void* context
)
126 const SchedulePair
* pair
= static_cast<const SchedulePair
*>(value
);
127 CFReadStreamRef stream
= (CFReadStreamRef
)context
;
129 CFReadStreamScheduleWithRunLoop(stream
, pair
->runLoop
, pair
->mode
);
132 static void advanceCurrentStream(FormStreamFields
*form
)
134 closeCurrentStream(form
);
136 if (form
->remainingElements
.isEmpty())
139 // Create the new stream.
140 FormDataElement
& nextInput
= form
->remainingElements
.last();
141 if (nextInput
.m_type
== FormDataElement::data
) {
142 size_t size
= nextInput
.m_data
.size();
143 char* data
= nextInput
.m_data
.releaseBuffer();
144 form
->currentStream
= CFReadStreamCreateWithBytesNoCopy(0, reinterpret_cast<const UInt8
*>(data
), size
, kCFAllocatorNull
);
145 form
->currentData
= data
;
147 CFStringRef filename
= nextInput
.m_filename
.createCFString();
149 CFURLRef fileURL
= CFURLCreateWithFileSystemPath(0, filename
, kCFURLWindowsPathStyle
, FALSE
);
151 CFURLRef fileURL
= CFURLCreateWithFileSystemPath(0, filename
, kCFURLPOSIXPathStyle
, FALSE
);
154 form
->currentStream
= CFReadStreamCreateWithFile(0, fileURL
);
157 form
->remainingElements
.removeLast();
159 // Set up the callback.
160 CFStreamClientContext context
= { 0, form
, NULL
, NULL
, NULL
};
161 CFReadStreamSetClient(form
->currentStream
, kCFStreamEventHasBytesAvailable
| kCFStreamEventErrorOccurred
| kCFStreamEventEndEncountered
,
162 formEventCallback
, &context
);
164 // Schedule with the current set of run loops.
165 CFSetApplyFunction(form
->scheduledRunLoopPairs
, scheduleWithPair
, form
->currentStream
);
168 static void openNextStream(FormStreamFields
* form
)
170 // Skip over any streams we can't open.
171 // For some purposes we might want to return an error, but the current CFURLConnection
172 // can't really do anything useful with an error at this point, so this is better.
173 advanceCurrentStream(form
);
174 while (form
->currentStream
&& !CFReadStreamOpen(form
->currentStream
))
175 advanceCurrentStream(form
);
178 static void* formCreate(CFReadStreamRef stream
, void* context
)
180 FormData
* formData
= static_cast<FormData
*>(context
);
182 CFSetCallBacks runLoopAndModeCallBacks
= { 0, pairRetain
, pairRelease
, NULL
, pairEqual
, pairHash
};
184 FormStreamFields
* newInfo
= new FormStreamFields
;
185 newInfo
->scheduledRunLoopPairs
= CFSetCreateMutable(0, 0, &runLoopAndModeCallBacks
);
186 newInfo
->currentStream
= NULL
;
187 newInfo
->currentData
= 0;
188 newInfo
->formStream
= stream
; // Don't retain. That would create a reference cycle.
190 // Append in reverse order since we remove elements from the end.
191 size_t size
= formData
->elements().size();
192 newInfo
->remainingElements
.reserveCapacity(size
);
193 for (size_t i
= 0; i
< size
; ++i
)
194 newInfo
->remainingElements
.append(formData
->elements()[size
- i
- 1]);
196 getStreamFormDatas().set(stream
, adoptRef(formData
));
201 static void formFinalize(CFReadStreamRef stream
, void* context
)
203 FormStreamFields
* form
= static_cast<FormStreamFields
*>(context
);
205 getStreamFormDatas().remove(stream
);
207 closeCurrentStream(form
);
208 CFRelease(form
->scheduledRunLoopPairs
);
212 static Boolean
formOpen(CFReadStreamRef stream
, CFStreamError
* error
, Boolean
* openComplete
, void* context
)
214 FormStreamFields
* form
= static_cast<FormStreamFields
*>(context
);
216 openNextStream(form
);
218 *openComplete
= TRUE
;
223 static CFIndex
formRead(CFReadStreamRef stream
, UInt8
* buffer
, CFIndex bufferLength
, CFStreamError
* error
, Boolean
* atEOF
, void* context
)
225 FormStreamFields
* form
= static_cast<FormStreamFields
*>(context
);
227 while (form
->currentStream
) {
228 CFIndex bytesRead
= CFReadStreamRead(form
->currentStream
, buffer
, bufferLength
);
230 *error
= CFReadStreamGetError(form
->currentStream
);
238 openNextStream(form
);
246 static Boolean
formCanRead(CFReadStreamRef stream
, void* context
)
248 FormStreamFields
* form
= static_cast<FormStreamFields
*>(context
);
250 while (form
->currentStream
&& CFReadStreamGetStatus(form
->currentStream
) == kCFStreamStatusAtEnd
) {
251 openNextStream(form
);
253 if (!form
->currentStream
) {
254 CFReadStreamSignalEvent(stream
, kCFStreamEventEndEncountered
, 0);
257 return CFReadStreamHasBytesAvailable(form
->currentStream
);
260 static void formClose(CFReadStreamRef stream
, void* context
)
262 FormStreamFields
* form
= static_cast<FormStreamFields
*>(context
);
264 closeCurrentStream(form
);
267 static void formSchedule(CFReadStreamRef stream
, CFRunLoopRef runLoop
, CFStringRef runLoopMode
, void* context
)
269 FormStreamFields
* form
= static_cast<FormStreamFields
*>(context
);
271 if (form
->currentStream
)
272 CFReadStreamScheduleWithRunLoop(form
->currentStream
, runLoop
, runLoopMode
);
273 SchedulePair pair
= { runLoop
, runLoopMode
};
274 CFSetAddValue(form
->scheduledRunLoopPairs
, &pair
);
277 static void formUnschedule(CFReadStreamRef stream
, CFRunLoopRef runLoop
, CFStringRef runLoopMode
, void* context
)
279 FormStreamFields
* form
= static_cast<FormStreamFields
*>(context
);
281 if (form
->currentStream
)
282 CFReadStreamUnscheduleFromRunLoop(form
->currentStream
, runLoop
, runLoopMode
);
283 SchedulePair pair
= { runLoop
, runLoopMode
};
284 CFSetRemoveValue(form
->scheduledRunLoopPairs
, &pair
);
287 static void formEventCallback(CFReadStreamRef stream
, CFStreamEventType type
, void* context
)
289 FormStreamFields
* form
= static_cast<FormStreamFields
*>(context
);
292 case kCFStreamEventHasBytesAvailable
:
293 CFReadStreamSignalEvent(form
->formStream
, kCFStreamEventHasBytesAvailable
, 0);
295 case kCFStreamEventErrorOccurred
: {
296 CFStreamError readStreamError
= CFReadStreamGetError(stream
);
297 CFReadStreamSignalEvent(form
->formStream
, kCFStreamEventErrorOccurred
, &readStreamError
);
300 case kCFStreamEventEndEncountered
:
301 openNextStream(form
);
302 if (!form
->currentStream
)
303 CFReadStreamSignalEvent(form
->formStream
, kCFStreamEventEndEncountered
, 0);
305 case kCFStreamEventNone
:
306 LOG_ERROR("unexpected kCFStreamEventNone");
308 case kCFStreamEventOpenCompleted
:
309 LOG_ERROR("unexpected kCFStreamEventOpenCompleted");
311 case kCFStreamEventCanAcceptBytes
:
312 LOG_ERROR("unexpected kCFStreamEventCanAcceptBytes");
317 void setHTTPBody(CFMutableURLRequestRef request
, PassRefPtr
<FormData
> formData
)
320 if (wkCanAccessCFURLRequestHTTPBodyParts())
321 wkCFURLRequestSetHTTPRequestBodyParts(request
, 0);
325 size_t count
= formData
->elements().size();
330 // Handle the common special case of one piece of form data, with no files.
332 const FormDataElement
& element
= formData
->elements()[0];
333 if (element
.m_type
== FormDataElement::data
) {
334 CFDataRef data
= CFDataCreate(0, reinterpret_cast<const UInt8
*>(element
.m_data
.data()), element
.m_data
.size());
335 CFURLRequestSetHTTPRequestBody(request
, data
);
341 if (wkCanAccessCFURLRequestHTTPBodyParts()) {
342 RetainPtr
<CFMutableArrayRef
> array(AdoptCF
, CFArrayCreateMutable(0, 0, &kCFTypeArrayCallBacks
));
344 for (size_t i
= 0; i
< count
; ++i
) {
345 const FormDataElement
& element
= formData
->elements()[i
];
346 if (element
.m_type
== FormDataElement::data
) {
347 RetainPtr
<CFDataRef
> data(AdoptCF
, CFDataCreate(0, reinterpret_cast<const UInt8
*>(element
.m_data
.data()), element
.m_data
.size()));
348 CFArrayAppendValue(array
.get(), data
.get());
350 RetainPtr
<CFStringRef
> filename(AdoptCF
, element
.m_filename
.createCFString());
351 CFArrayAppendValue(array
.get(), filename
.get());
355 wkCFURLRequestSetHTTPRequestBodyParts(request
, array
.get());
359 // Precompute the content length so CFURLConnection doesn't use chunked mode.
360 bool haveLength
= true;
361 long long length
= 0;
362 for (size_t i
= 0; i
< count
; ++i
) {
363 const FormDataElement
& element
= formData
->elements()[i
];
364 if (element
.m_type
== FormDataElement::data
)
365 length
+= element
.m_data
.size();
368 if (getFileSize(element
.m_filename
, size
))
376 CFStringRef lengthStr
= CFStringCreateWithFormat(0, 0, CFSTR("%lld"), length
);
377 CFURLRequestSetHTTPHeaderFieldValue(request
, CFSTR("Content-Length"), lengthStr
);
378 CFRelease(lengthStr
);
381 static WCReadStreamCallBacks formDataStreamCallbacks
=
382 { 1, formCreate
, formFinalize
, 0, formOpen
, 0, formRead
, 0, formCanRead
, formClose
, 0, 0, 0, formSchedule
, formUnschedule
};
384 CFReadStreamRef stream
= CFReadStreamCreate(0, (CFReadStreamCallBacks
*)&formDataStreamCallbacks
, formData
.releaseRef());
385 CFURLRequestSetHTTPRequestBodyStream(request
, stream
);
389 PassRefPtr
<FormData
> httpBodyFromRequest(CFURLRequestRef request
)
391 if (RetainPtr
<CFDataRef
> bodyData
= CFURLRequestCopyHTTPRequestBody(request
))
392 return FormData::create(CFDataGetBytePtr(bodyData
.get()), CFDataGetLength(bodyData
.get()));
394 if (wkCanAccessCFURLRequestHTTPBodyParts()) {
395 if (RetainPtr
<CFArrayRef
> bodyParts
= wkCFURLRequestCopyHTTPRequestBodyParts(request
)) {
396 RefPtr
<FormData
> formData
= FormData::create();
398 CFIndex count
= CFArrayGetCount(bodyParts
.get());
399 for (CFIndex i
= 0; i
< count
; i
++) {
400 CFTypeRef bodyPart
= CFArrayGetValueAtIndex(bodyParts
.get(), i
);
401 CFTypeID typeID
= CFGetTypeID(bodyPart
);
402 if (typeID
== CFStringGetTypeID()) {
403 String filename
= (CFStringRef
)bodyPart
;
404 formData
->appendFile(filename
);
405 } else if (typeID
== CFDataGetTypeID()) {
406 CFDataRef data
= (CFDataRef
)bodyPart
;
407 formData
->appendData(CFDataGetBytePtr(data
), CFDataGetLength(data
));
409 ASSERT_NOT_REACHED();
411 return formData
.release();
414 if (RetainPtr
<CFReadStreamRef
> bodyStream
= CFURLRequestCopyHTTPRequestBodyStream(request
))
415 return getStreamFormDatas().get(bodyStream
.get());
418 // FIXME: what to do about arbitrary body streams?