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, const FormData*>* streamFormDatas = 0;
49 static HashMap<CFReadStreamRef, const FormData*>* getStreamFormDatas()
52 streamFormDatas = new HashMap<CFReadStreamRef, const FormData*>();
53 return streamFormDatas;
56 static void formEventCallback(CFReadStreamRef stream, CFStreamEventType type, void* context);
58 struct FormStreamFields {
59 CFMutableSetRef scheduledRunLoopPairs;
60 Vector<FormDataElement> remainingElements; // in reverse order
61 CFReadStreamRef currentStream;
63 CFReadStreamRef formStream;
71 static const void* pairRetain(CFAllocatorRef alloc, const void* value)
73 const SchedulePair* pair = static_cast<const SchedulePair*>(value);
75 SchedulePair* result = new SchedulePair;
76 CFRetain(pair->runLoop);
77 result->runLoop = pair->runLoop;
78 result->mode = CFStringCreateCopy(alloc, pair->mode);
82 static void pairRelease(CFAllocatorRef alloc, const void* value)
84 const SchedulePair* pair = static_cast<const SchedulePair*>(value);
86 CFRelease(pair->runLoop);
87 CFRelease(pair->mode);
91 static Boolean pairEqual(const void* a, const void* b)
93 const SchedulePair* pairA = static_cast<const SchedulePair*>(a);
94 const SchedulePair* pairB = static_cast<const SchedulePair*>(b);
96 return pairA->runLoop == pairB->runLoop && CFEqual(pairA->mode, pairB->mode);
99 static CFHashCode pairHash(const void* value)
101 const SchedulePair* pair = static_cast<const SchedulePair*>(value);
103 return (CFHashCode)pair->runLoop ^ CFHash(pair->mode);
106 static void closeCurrentStream(FormStreamFields *form)
108 if (form->currentStream) {
109 CFReadStreamClose(form->currentStream);
110 CFReadStreamSetClient(form->currentStream, kCFStreamEventNone, NULL, NULL);
111 CFRelease(form->currentStream);
112 form->currentStream = NULL;
114 if (form->currentData) {
115 fastFree(form->currentData);
116 form->currentData = 0;
120 static void scheduleWithPair(const void* value, void* context)
122 const SchedulePair* pair = static_cast<const SchedulePair*>(value);
123 CFReadStreamRef stream = (CFReadStreamRef)context;
125 CFReadStreamScheduleWithRunLoop(stream, pair->runLoop, pair->mode);
128 static void advanceCurrentStream(FormStreamFields *form)
130 closeCurrentStream(form);
132 if (form->remainingElements.isEmpty())
135 // Create the new stream.
136 FormDataElement& nextInput = form->remainingElements.last();
137 if (nextInput.m_type == FormDataElement::data) {
138 size_t size = nextInput.m_data.size();
139 char* data = nextInput.m_data.releaseBuffer();
140 form->currentStream = CFReadStreamCreateWithBytesNoCopy(0, reinterpret_cast<const UInt8*>(data), size, kCFAllocatorNull);
141 form->currentData = data;
143 CFStringRef filename = nextInput.m_filename.createCFString();
144 CFURLRef fileURL = CFURLCreateWithFileSystemPath(0, filename, kCFURLPOSIXPathStyle, FALSE);
146 form->currentStream = CFReadStreamCreateWithFile(0, fileURL);
149 form->remainingElements.removeLast();
151 // Set up the callback.
152 CFStreamClientContext context = { 0, form, NULL, NULL, NULL };
153 CFReadStreamSetClient(form->currentStream, kCFStreamEventHasBytesAvailable | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered,
154 formEventCallback, &context);
156 // Schedule with the current set of run loops.
157 CFSetApplyFunction(form->scheduledRunLoopPairs, scheduleWithPair, form->currentStream);
160 static void openNextStream(FormStreamFields* form)
162 // Skip over any streams we can't open.
163 // For some purposes we might want to return an error, but the current CFURLConnection
164 // can't really do anything useful with an error at this point, so this is better.
165 advanceCurrentStream(form);
166 while (form->currentStream && !CFReadStreamOpen(form->currentStream))
167 advanceCurrentStream(form);
170 static void* formCreate(CFReadStreamRef stream, void* context)
172 const FormData* formData = static_cast<const FormData*>(context);
174 CFSetCallBacks runLoopAndModeCallBacks = { 0, pairRetain, pairRelease, NULL, pairEqual, pairHash };
176 FormStreamFields* newInfo = new FormStreamFields;
177 newInfo->scheduledRunLoopPairs = CFSetCreateMutable(0, 0, &runLoopAndModeCallBacks);
178 newInfo->currentStream = NULL;
179 newInfo->currentData = 0;
180 newInfo->formStream = stream; // Don't retain. That would create a reference cycle.
182 // Append in reverse order since we remove elements from the end.
183 size_t size = formData->elements().size();
184 newInfo->remainingElements.reserveCapacity(size);
185 for (size_t i = 0; i < size; ++i)
186 newInfo->remainingElements.append(formData->elements()[size - i - 1]);
188 getStreamFormDatas()->set(stream, new FormData(*formData));
193 static void formFinalize(CFReadStreamRef stream, void* context)
195 FormStreamFields* form = static_cast<FormStreamFields*>(context);
197 delete getStreamFormDatas()->get(stream);
198 getStreamFormDatas()->remove(stream);
200 closeCurrentStream(form);
201 CFRelease(form->scheduledRunLoopPairs);
205 static Boolean formOpen(CFReadStreamRef stream, CFStreamError* error, Boolean* openComplete, void* context)
207 FormStreamFields* form = static_cast<FormStreamFields*>(context);
209 openNextStream(form);
211 *openComplete = TRUE;
216 static CFIndex formRead(CFReadStreamRef stream, UInt8* buffer, CFIndex bufferLength, CFStreamError* error, Boolean* atEOF, void* context)
218 FormStreamFields* form = static_cast<FormStreamFields*>(context);
220 while (form->currentStream) {
221 CFIndex bytesRead = CFReadStreamRead(form->currentStream, buffer, bufferLength);
223 *error = CFReadStreamGetError(form->currentStream);
231 openNextStream(form);
239 static Boolean formCanRead(CFReadStreamRef stream, void* context)
241 FormStreamFields* form = static_cast<FormStreamFields*>(context);
243 while (form->currentStream && CFReadStreamGetStatus(form->currentStream) == kCFStreamStatusAtEnd) {
244 openNextStream(form);
246 if (!form->currentStream) {
247 CFReadStreamSignalEvent(stream, kCFStreamEventEndEncountered, 0);
250 return CFReadStreamHasBytesAvailable(form->currentStream);
253 static void formClose(CFReadStreamRef stream, void* context)
255 FormStreamFields* form = static_cast<FormStreamFields*>(context);
257 closeCurrentStream(form);
260 static void formSchedule(CFReadStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void* context)
262 FormStreamFields* form = static_cast<FormStreamFields*>(context);
264 if (form->currentStream)
265 CFReadStreamScheduleWithRunLoop(form->currentStream, runLoop, runLoopMode);
266 SchedulePair pair = { runLoop, runLoopMode };
267 CFSetAddValue(form->scheduledRunLoopPairs, &pair);
270 static void formUnschedule(CFReadStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void* context)
272 FormStreamFields* form = static_cast<FormStreamFields*>(context);
274 if (form->currentStream)
275 CFReadStreamUnscheduleFromRunLoop(form->currentStream, runLoop, runLoopMode);
276 SchedulePair pair = { runLoop, runLoopMode };
277 CFSetRemoveValue(form->scheduledRunLoopPairs, &pair);
280 static void formEventCallback(CFReadStreamRef stream, CFStreamEventType type, void* context)
282 FormStreamFields* form = static_cast<FormStreamFields*>(context);
285 case kCFStreamEventHasBytesAvailable:
286 CFReadStreamSignalEvent(form->formStream, kCFStreamEventHasBytesAvailable, 0);
288 case kCFStreamEventErrorOccurred: {
289 CFStreamError readStreamError = CFReadStreamGetError(stream);
290 CFReadStreamSignalEvent(form->formStream, kCFStreamEventErrorOccurred, &readStreamError);
293 case kCFStreamEventEndEncountered:
294 openNextStream(form);
295 if (!form->currentStream)
296 CFReadStreamSignalEvent(form->formStream, kCFStreamEventEndEncountered, 0);
298 case kCFStreamEventNone:
299 LOG_ERROR("unexpected kCFStreamEventNone");
301 case kCFStreamEventOpenCompleted:
302 LOG_ERROR("unexpected kCFStreamEventOpenCompleted");
304 case kCFStreamEventCanAcceptBytes:
305 LOG_ERROR("unexpected kCFStreamEventCanAcceptBytes");
310 void setHTTPBody(CFMutableURLRequestRef request, const FormData& formData)
312 size_t count = formData.elements().size();
317 // Handle the common special case of one piece of form data, with no files.
319 const FormDataElement& element = formData.elements()[0];
320 if (element.m_type == FormDataElement::data) {
321 CFDataRef data = CFDataCreate(0, reinterpret_cast<const UInt8 *>(element.m_data.data()), element.m_data.size());
322 CFURLRequestSetHTTPRequestBody(request, data);
328 // Precompute the content length so CFURLConnection doesn't use chunked mode.
329 bool haveLength = true;
330 long long length = 0;
331 for (size_t i = 0; i < count; ++i) {
332 const FormDataElement& element = formData.elements()[i];
333 if (element.m_type == FormDataElement::data)
334 length += element.m_data.size();
336 struct _stat64i32 sb;
337 int statResult = _stat(element.m_filename.utf8(), &sb);
338 if (statResult == 0 && (sb.st_mode & S_IFMT) == S_IFREG)
339 length += sb.st_size;
346 CFStringRef lengthStr = CFStringCreateWithFormat(0, 0, CFSTR("%lld"), length);
347 CFURLRequestSetHTTPHeaderFieldValue(request, CFSTR("Content-Length"), lengthStr);
348 CFRelease(lengthStr);
351 static CFReadStreamCallBacks formDataStreamCallbacks =
352 { 1, formCreate, formFinalize, 0, formOpen, 0, formRead, 0, formCanRead, formClose, 0, 0, 0, formSchedule, formUnschedule};
354 CFReadStreamRef stream = CFReadStreamCreate(0, &formDataStreamCallbacks, const_cast<FormData*>(&formData));
355 CFURLRequestSetHTTPRequestBodyStream(request, stream);
360 const FormData* httpBodyFromStream(CFReadStreamRef stream)
362 return getStreamFormDatas()->get(stream);