cdc5e049fea741cc67a3c43d46a6e6bee22e55e1
[WebKit-https.git] / WebCore / platform / network / cf / FormDataStreamCFNet.cpp
1 // -*- mode: c++; c-basic-offset: 4 -*-
2 /*
3  * Copyright (C) 2005, 2006 Apple Computer, Inc.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
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. 
17  *
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.
28  */
29
30 /* originally written by Becky Willrich, additional code by Darin Adler */
31
32 #include "config.h"
33 #include "FormDataStreamCFNet.h"
34
35 #include "CString.h"
36 #include "FormData.h"
37 #include <CFNetwork/CFURLRequestPriv.h>
38 #include <CoreFoundation/CFStreamAbstract.h>
39 #include <sys/stat.h>
40 #include <sys/types.h>
41 #include <wtf/Assertions.h>
42 #include <wtf/HashMap.h>
43
44
45 namespace WebCore {
46
47 static HashMap<CFReadStreamRef, const FormData*>* streamFormDatas = 0;
48
49 static HashMap<CFReadStreamRef, const FormData*>* getStreamFormDatas()
50 {
51     if (!streamFormDatas)
52         streamFormDatas = new HashMap<CFReadStreamRef, const FormData*>();
53     return streamFormDatas;
54 }
55
56 static void formEventCallback(CFReadStreamRef stream, CFStreamEventType type, void* context);
57
58 struct FormStreamFields {
59     CFMutableSetRef scheduledRunLoopPairs;
60     Vector<FormDataElement> remainingElements; // in reverse order
61     CFReadStreamRef currentStream;
62     char* currentData;
63     CFReadStreamRef formStream;
64 };
65
66 struct SchedulePair {
67     CFRunLoopRef runLoop;
68     CFStringRef mode;
69 };
70
71 static const void* pairRetain(CFAllocatorRef alloc, const void* value)
72 {
73     const SchedulePair* pair = static_cast<const SchedulePair*>(value);
74
75     SchedulePair* result = new SchedulePair;
76     CFRetain(pair->runLoop);
77     result->runLoop = pair->runLoop;
78     result->mode = CFStringCreateCopy(alloc, pair->mode);
79     return result;
80 }
81
82 static void pairRelease(CFAllocatorRef alloc, const void* value)
83 {
84     const SchedulePair* pair = static_cast<const SchedulePair*>(value);
85
86     CFRelease(pair->runLoop);
87     CFRelease(pair->mode);
88     delete pair;
89 }
90
91 static Boolean pairEqual(const void* a, const void* b)
92 {
93     const SchedulePair* pairA = static_cast<const SchedulePair*>(a);
94     const SchedulePair* pairB = static_cast<const SchedulePair*>(b);
95
96     return pairA->runLoop == pairB->runLoop && CFEqual(pairA->mode, pairB->mode);
97 }
98
99 static CFHashCode pairHash(const void* value)
100 {
101     const SchedulePair* pair = static_cast<const SchedulePair*>(value);
102
103     return (CFHashCode)pair->runLoop ^ CFHash(pair->mode);
104 }
105
106 static void closeCurrentStream(FormStreamFields *form)
107 {
108     if (form->currentStream) {
109         CFReadStreamClose(form->currentStream);
110         CFReadStreamSetClient(form->currentStream, kCFStreamEventNone, NULL, NULL);
111         CFRelease(form->currentStream);
112         form->currentStream = NULL;
113     }
114     if (form->currentData) {
115         fastFree(form->currentData);
116         form->currentData = 0;
117     }
118 }
119
120 static void scheduleWithPair(const void* value, void* context)
121 {
122     const SchedulePair* pair = static_cast<const SchedulePair*>(value);
123     CFReadStreamRef stream = (CFReadStreamRef)context;
124
125     CFReadStreamScheduleWithRunLoop(stream, pair->runLoop, pair->mode);
126 }
127
128 static void advanceCurrentStream(FormStreamFields *form)
129 {
130     closeCurrentStream(form);
131
132     if (form->remainingElements.isEmpty())
133         return;
134
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;
142     } else {
143         CFStringRef filename = nextInput.m_filename.createCFString();
144         CFURLRef fileURL = CFURLCreateWithFileSystemPath(0, filename, kCFURLPOSIXPathStyle, FALSE);
145         CFRelease(filename);
146         form->currentStream = CFReadStreamCreateWithFile(0, fileURL);
147         CFRelease(fileURL);
148     }
149     form->remainingElements.removeLast();
150
151     // Set up the callback.
152     CFStreamClientContext context = { 0, form, NULL, NULL, NULL };
153     CFReadStreamSetClient(form->currentStream, kCFStreamEventHasBytesAvailable | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered,
154         formEventCallback, &context);
155
156     // Schedule with the current set of run loops.
157     CFSetApplyFunction(form->scheduledRunLoopPairs, scheduleWithPair, form->currentStream);
158 }
159
160 static void openNextStream(FormStreamFields* form)
161 {
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);
168 }
169
170 static void* formCreate(CFReadStreamRef stream, void* context)
171 {
172     const FormData* formData = static_cast<const FormData*>(context);
173
174     CFSetCallBacks runLoopAndModeCallBacks = { 0, pairRetain, pairRelease, NULL, pairEqual, pairHash };
175
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.
181
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]);
187
188     getStreamFormDatas()->set(stream, new FormData(*formData));
189
190     return newInfo;
191 }
192
193 static void formFinalize(CFReadStreamRef stream, void* context)
194 {
195     FormStreamFields* form = static_cast<FormStreamFields*>(context);
196
197     delete getStreamFormDatas()->get(stream);
198     getStreamFormDatas()->remove(stream);
199
200     closeCurrentStream(form);
201     CFRelease(form->scheduledRunLoopPairs);
202     delete form;
203 }
204
205 static Boolean formOpen(CFReadStreamRef stream, CFStreamError* error, Boolean* openComplete, void* context)
206 {
207     FormStreamFields* form = static_cast<FormStreamFields*>(context);
208
209     openNextStream(form);
210
211     *openComplete = TRUE;
212     error->error = 0;
213     return TRUE;
214 }
215
216 static CFIndex formRead(CFReadStreamRef stream, UInt8* buffer, CFIndex bufferLength, CFStreamError* error, Boolean* atEOF, void* context)
217 {
218     FormStreamFields* form = static_cast<FormStreamFields*>(context);
219
220     while (form->currentStream) {
221         CFIndex bytesRead = CFReadStreamRead(form->currentStream, buffer, bufferLength);
222         if (bytesRead < 0) {
223             *error = CFReadStreamGetError(form->currentStream);
224             return -1;
225         }
226         if (bytesRead > 0) {
227             error->error = 0;
228             *atEOF = FALSE;
229             return bytesRead;
230         }
231         openNextStream(form);
232     }
233
234     error->error = 0;
235     *atEOF = TRUE;
236     return 0;
237 }
238
239 static Boolean formCanRead(CFReadStreamRef stream, void* context)
240 {
241     FormStreamFields* form = static_cast<FormStreamFields*>(context);
242
243     while (form->currentStream && CFReadStreamGetStatus(form->currentStream) == kCFStreamStatusAtEnd) {
244         openNextStream(form);
245     }
246     if (!form->currentStream) {
247         CFReadStreamSignalEvent(stream, kCFStreamEventEndEncountered, 0);
248         return FALSE;
249     }
250     return CFReadStreamHasBytesAvailable(form->currentStream);
251 }
252
253 static void formClose(CFReadStreamRef stream, void* context)
254 {
255     FormStreamFields* form = static_cast<FormStreamFields*>(context);
256
257     closeCurrentStream(form);
258 }
259
260 static void formSchedule(CFReadStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void* context)
261 {
262     FormStreamFields* form = static_cast<FormStreamFields*>(context);
263
264     if (form->currentStream)
265         CFReadStreamScheduleWithRunLoop(form->currentStream, runLoop, runLoopMode);
266     SchedulePair pair = { runLoop, runLoopMode };
267     CFSetAddValue(form->scheduledRunLoopPairs, &pair);
268 }
269
270 static void formUnschedule(CFReadStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void* context)
271 {
272     FormStreamFields* form = static_cast<FormStreamFields*>(context);
273
274     if (form->currentStream)
275         CFReadStreamUnscheduleFromRunLoop(form->currentStream, runLoop, runLoopMode);
276     SchedulePair pair = { runLoop, runLoopMode };
277     CFSetRemoveValue(form->scheduledRunLoopPairs, &pair);
278 }
279
280 static void formEventCallback(CFReadStreamRef stream, CFStreamEventType type, void* context)
281 {
282     FormStreamFields* form = static_cast<FormStreamFields*>(context);
283
284     switch (type) {
285     case kCFStreamEventHasBytesAvailable:
286         CFReadStreamSignalEvent(form->formStream, kCFStreamEventHasBytesAvailable, 0);
287         break;
288     case kCFStreamEventErrorOccurred: {
289         CFStreamError readStreamError = CFReadStreamGetError(stream);
290         CFReadStreamSignalEvent(form->formStream, kCFStreamEventErrorOccurred, &readStreamError);
291         break;
292     }
293     case kCFStreamEventEndEncountered:
294         openNextStream(form);
295         if (!form->currentStream)
296             CFReadStreamSignalEvent(form->formStream, kCFStreamEventEndEncountered, 0);
297         break;
298     case kCFStreamEventNone:
299         LOG_ERROR("unexpected kCFStreamEventNone");
300         break;
301     case kCFStreamEventOpenCompleted:
302         LOG_ERROR("unexpected kCFStreamEventOpenCompleted");
303         break;
304     case kCFStreamEventCanAcceptBytes:
305         LOG_ERROR("unexpected kCFStreamEventCanAcceptBytes");
306         break;
307     }
308 }
309
310 void setHTTPBody(CFMutableURLRequestRef request, const FormData& formData)
311 {
312     size_t count = formData.elements().size();
313
314     if (count == 0)
315         return;
316
317     // Handle the common special case of one piece of form data, with no files.
318     if (count == 1) {
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);
323             CFRelease(data);
324             return;
325         }
326     }
327
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();
335         else {
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;
340             else
341                 haveLength = false;
342         }
343     }
344
345     if (haveLength) {
346         CFStringRef lengthStr = CFStringCreateWithFormat(0, 0, CFSTR("%lld"), length);
347         CFURLRequestSetHTTPHeaderFieldValue(request, CFSTR("Content-Length"), lengthStr);
348         CFRelease(lengthStr);
349     }
350
351     static CFReadStreamCallBacks formDataStreamCallbacks = 
352         { 1, formCreate, formFinalize, 0, formOpen, 0, formRead, 0, formCanRead, formClose, 0, 0, 0, formSchedule, formUnschedule};
353
354     CFReadStreamRef stream = CFReadStreamCreate(0, &formDataStreamCallbacks, const_cast<FormData*>(&formData));
355     CFURLRequestSetHTTPRequestBodyStream(request, stream);
356     CFRelease(stream);
357 }
358
359
360 const FormData* httpBodyFromStream(CFReadStreamRef stream)
361 {
362     return getStreamFormDatas()->get(stream);
363 }
364
365 }