1 // -*- mode: c++; c-basic-offset: 4 -*-
3 * Copyright (C) 2005, 2006 Apple Computer, Inc. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
15 * its contributors may be used to endorse or promote products derived
16 * from this software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 /* originally written by Becky Willrich, additional code by Darin Adler */
33 #include "FormDataStreamCFNet.h"
37 #include <CFNetwork/CFURLRequestPriv.h>
38 #include <CoreFoundation/CFStreamAbstract.h>
40 #include <sys/types.h>
41 #include <wtf/Assertions.h>
42 #include <wtf/HashMap.h>
47 static HashMap<CFReadStreamRef, RefPtr<FormData> >& getStreamFormDatas()
49 static HashMap<CFReadStreamRef, RefPtr<FormData> > streamFormDatas;
50 return streamFormDatas;
53 static void formEventCallback(CFReadStreamRef stream, CFStreamEventType type, void* context);
55 struct FormStreamFields {
56 CFMutableSetRef scheduledRunLoopPairs;
57 Vector<FormDataElement> remainingElements; // in reverse order
58 CFReadStreamRef currentStream;
60 CFReadStreamRef formStream;
68 static const void* pairRetain(CFAllocatorRef alloc, const void* value)
70 const SchedulePair* pair = static_cast<const SchedulePair*>(value);
72 SchedulePair* result = new SchedulePair;
73 CFRetain(pair->runLoop);
74 result->runLoop = pair->runLoop;
75 result->mode = CFStringCreateCopy(alloc, pair->mode);
79 static void pairRelease(CFAllocatorRef alloc, const void* value)
81 const SchedulePair* pair = static_cast<const SchedulePair*>(value);
83 CFRelease(pair->runLoop);
84 CFRelease(pair->mode);
88 static Boolean pairEqual(const void* a, const void* b)
90 const SchedulePair* pairA = static_cast<const SchedulePair*>(a);
91 const SchedulePair* pairB = static_cast<const SchedulePair*>(b);
93 return pairA->runLoop == pairB->runLoop && CFEqual(pairA->mode, pairB->mode);
96 static CFHashCode pairHash(const void* value)
98 const SchedulePair* pair = static_cast<const SchedulePair*>(value);
100 return (CFHashCode)pair->runLoop ^ CFHash(pair->mode);
103 static void closeCurrentStream(FormStreamFields *form)
105 if (form->currentStream) {
106 CFReadStreamClose(form->currentStream);
107 CFReadStreamSetClient(form->currentStream, kCFStreamEventNone, NULL, NULL);
108 CFRelease(form->currentStream);
109 form->currentStream = NULL;
111 if (form->currentData) {
112 fastFree(form->currentData);
113 form->currentData = 0;
117 static void scheduleWithPair(const void* value, void* context)
119 const SchedulePair* pair = static_cast<const SchedulePair*>(value);
120 CFReadStreamRef stream = (CFReadStreamRef)context;
122 CFReadStreamScheduleWithRunLoop(stream, pair->runLoop, pair->mode);
125 static void advanceCurrentStream(FormStreamFields *form)
127 closeCurrentStream(form);
129 if (form->remainingElements.isEmpty())
132 // Create the new stream.
133 FormDataElement& nextInput = form->remainingElements.last();
134 if (nextInput.m_type == FormDataElement::data) {
135 size_t size = nextInput.m_data.size();
136 char* data = nextInput.m_data.releaseBuffer();
137 form->currentStream = CFReadStreamCreateWithBytesNoCopy(0, reinterpret_cast<const UInt8*>(data), size, kCFAllocatorNull);
138 form->currentData = data;
140 CFStringRef filename = nextInput.m_filename.createCFString();
141 CFURLRef fileURL = CFURLCreateWithFileSystemPath(0, filename, kCFURLPOSIXPathStyle, FALSE);
143 form->currentStream = CFReadStreamCreateWithFile(0, fileURL);
146 form->remainingElements.removeLast();
148 // Set up the callback.
149 CFStreamClientContext context = { 0, form, NULL, NULL, NULL };
150 CFReadStreamSetClient(form->currentStream, kCFStreamEventHasBytesAvailable | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered,
151 formEventCallback, &context);
153 // Schedule with the current set of run loops.
154 CFSetApplyFunction(form->scheduledRunLoopPairs, scheduleWithPair, form->currentStream);
157 static void openNextStream(FormStreamFields* form)
159 // Skip over any streams we can't open.
160 // For some purposes we might want to return an error, but the current CFURLConnection
161 // can't really do anything useful with an error at this point, so this is better.
162 advanceCurrentStream(form);
163 while (form->currentStream && !CFReadStreamOpen(form->currentStream))
164 advanceCurrentStream(form);
167 static void* formCreate(CFReadStreamRef stream, void* context)
169 FormData* formData = static_cast<FormData*>(context);
171 CFSetCallBacks runLoopAndModeCallBacks = { 0, pairRetain, pairRelease, NULL, pairEqual, pairHash };
173 FormStreamFields* newInfo = new FormStreamFields;
174 newInfo->scheduledRunLoopPairs = CFSetCreateMutable(0, 0, &runLoopAndModeCallBacks);
175 newInfo->currentStream = NULL;
176 newInfo->currentData = 0;
177 newInfo->formStream = stream; // Don't retain. That would create a reference cycle.
179 // Append in reverse order since we remove elements from the end.
180 size_t size = formData->elements().size();
181 newInfo->remainingElements.reserveCapacity(size);
182 for (size_t i = 0; i < size; ++i)
183 newInfo->remainingElements.append(formData->elements()[size - i - 1]);
185 getStreamFormDatas().set(stream, adoptRef(formData));
190 static void formFinalize(CFReadStreamRef stream, void* context)
192 FormStreamFields* form = static_cast<FormStreamFields*>(context);
194 getStreamFormDatas().remove(stream);
196 closeCurrentStream(form);
197 CFRelease(form->scheduledRunLoopPairs);
201 static Boolean formOpen(CFReadStreamRef stream, CFStreamError* error, Boolean* openComplete, void* context)
203 FormStreamFields* form = static_cast<FormStreamFields*>(context);
205 openNextStream(form);
207 *openComplete = TRUE;
212 static CFIndex formRead(CFReadStreamRef stream, UInt8* buffer, CFIndex bufferLength, CFStreamError* error, Boolean* atEOF, void* context)
214 FormStreamFields* form = static_cast<FormStreamFields*>(context);
216 while (form->currentStream) {
217 CFIndex bytesRead = CFReadStreamRead(form->currentStream, buffer, bufferLength);
219 *error = CFReadStreamGetError(form->currentStream);
227 openNextStream(form);
235 static Boolean formCanRead(CFReadStreamRef stream, void* context)
237 FormStreamFields* form = static_cast<FormStreamFields*>(context);
239 while (form->currentStream && CFReadStreamGetStatus(form->currentStream) == kCFStreamStatusAtEnd) {
240 openNextStream(form);
242 if (!form->currentStream) {
243 CFReadStreamSignalEvent(stream, kCFStreamEventEndEncountered, 0);
246 return CFReadStreamHasBytesAvailable(form->currentStream);
249 static void formClose(CFReadStreamRef stream, void* context)
251 FormStreamFields* form = static_cast<FormStreamFields*>(context);
253 closeCurrentStream(form);
256 static void formSchedule(CFReadStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void* context)
258 FormStreamFields* form = static_cast<FormStreamFields*>(context);
260 if (form->currentStream)
261 CFReadStreamScheduleWithRunLoop(form->currentStream, runLoop, runLoopMode);
262 SchedulePair pair = { runLoop, runLoopMode };
263 CFSetAddValue(form->scheduledRunLoopPairs, &pair);
266 static void formUnschedule(CFReadStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void* context)
268 FormStreamFields* form = static_cast<FormStreamFields*>(context);
270 if (form->currentStream)
271 CFReadStreamUnscheduleFromRunLoop(form->currentStream, runLoop, runLoopMode);
272 SchedulePair pair = { runLoop, runLoopMode };
273 CFSetRemoveValue(form->scheduledRunLoopPairs, &pair);
276 static void formEventCallback(CFReadStreamRef stream, CFStreamEventType type, void* context)
278 FormStreamFields* form = static_cast<FormStreamFields*>(context);
281 case kCFStreamEventHasBytesAvailable:
282 CFReadStreamSignalEvent(form->formStream, kCFStreamEventHasBytesAvailable, 0);
284 case kCFStreamEventErrorOccurred: {
285 CFStreamError readStreamError = CFReadStreamGetError(stream);
286 CFReadStreamSignalEvent(form->formStream, kCFStreamEventErrorOccurred, &readStreamError);
289 case kCFStreamEventEndEncountered:
290 openNextStream(form);
291 if (!form->currentStream)
292 CFReadStreamSignalEvent(form->formStream, kCFStreamEventEndEncountered, 0);
294 case kCFStreamEventNone:
295 LOG_ERROR("unexpected kCFStreamEventNone");
297 case kCFStreamEventOpenCompleted:
298 LOG_ERROR("unexpected kCFStreamEventOpenCompleted");
300 case kCFStreamEventCanAcceptBytes:
301 LOG_ERROR("unexpected kCFStreamEventCanAcceptBytes");
306 void setHTTPBody(CFMutableURLRequestRef request, PassRefPtr<FormData> formData)
308 size_t count = formData->elements().size();
313 // Handle the common special case of one piece of form data, with no files.
315 const FormDataElement& element = formData->elements()[0];
316 if (element.m_type == FormDataElement::data) {
317 CFDataRef data = CFDataCreate(0, reinterpret_cast<const UInt8 *>(element.m_data.data()), element.m_data.size());
318 CFURLRequestSetHTTPRequestBody(request, data);
324 // Precompute the content length so CFURLConnection doesn't use chunked mode.
325 bool haveLength = true;
326 long long length = 0;
327 for (size_t i = 0; i < count; ++i) {
328 const FormDataElement& element = formData->elements()[i];
329 if (element.m_type == FormDataElement::data)
330 length += element.m_data.size();
332 struct _stat64i32 sb;
333 int statResult = _stat(element.m_filename.utf8(), &sb);
334 if (statResult == 0 && (sb.st_mode & S_IFMT) == S_IFREG)
335 length += sb.st_size;
342 CFStringRef lengthStr = CFStringCreateWithFormat(0, 0, CFSTR("%lld"), length);
343 CFURLRequestSetHTTPHeaderFieldValue(request, CFSTR("Content-Length"), lengthStr);
344 CFRelease(lengthStr);
347 static CFReadStreamCallBacks formDataStreamCallbacks =
348 { 1, formCreate, formFinalize, 0, formOpen, 0, formRead, 0, formCanRead, formClose, 0, 0, 0, formSchedule, formUnschedule};
350 CFReadStreamRef stream = CFReadStreamCreate(0, &formDataStreamCallbacks, formData.releaseRef());
351 CFURLRequestSetHTTPRequestBodyStream(request, stream);
356 FormData* httpBodyFromStream(CFReadStreamRef stream)
358 return getStreamFormDatas().get(stream).get();