5fbd1aee2ab4911ebcb552b4a75bb3e0711e5ba6
[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, RefPtr<FormData> >& getStreamFormDatas()
48 {
49     static HashMap<CFReadStreamRef, RefPtr<FormData> > streamFormDatas;
50     return streamFormDatas;
51 }
52
53 static void formEventCallback(CFReadStreamRef stream, CFStreamEventType type, void* context);
54
55 struct FormStreamFields {
56     CFMutableSetRef scheduledRunLoopPairs;
57     Vector<FormDataElement> remainingElements; // in reverse order
58     CFReadStreamRef currentStream;
59     char* currentData;
60     CFReadStreamRef formStream;
61 };
62
63 struct SchedulePair {
64     CFRunLoopRef runLoop;
65     CFStringRef mode;
66 };
67
68 static const void* pairRetain(CFAllocatorRef alloc, const void* value)
69 {
70     const SchedulePair* pair = static_cast<const SchedulePair*>(value);
71
72     SchedulePair* result = new SchedulePair;
73     CFRetain(pair->runLoop);
74     result->runLoop = pair->runLoop;
75     result->mode = CFStringCreateCopy(alloc, pair->mode);
76     return result;
77 }
78
79 static void pairRelease(CFAllocatorRef alloc, const void* value)
80 {
81     const SchedulePair* pair = static_cast<const SchedulePair*>(value);
82
83     CFRelease(pair->runLoop);
84     CFRelease(pair->mode);
85     delete pair;
86 }
87
88 static Boolean pairEqual(const void* a, const void* b)
89 {
90     const SchedulePair* pairA = static_cast<const SchedulePair*>(a);
91     const SchedulePair* pairB = static_cast<const SchedulePair*>(b);
92
93     return pairA->runLoop == pairB->runLoop && CFEqual(pairA->mode, pairB->mode);
94 }
95
96 static CFHashCode pairHash(const void* value)
97 {
98     const SchedulePair* pair = static_cast<const SchedulePair*>(value);
99
100     return (CFHashCode)pair->runLoop ^ CFHash(pair->mode);
101 }
102
103 static void closeCurrentStream(FormStreamFields *form)
104 {
105     if (form->currentStream) {
106         CFReadStreamClose(form->currentStream);
107         CFReadStreamSetClient(form->currentStream, kCFStreamEventNone, NULL, NULL);
108         CFRelease(form->currentStream);
109         form->currentStream = NULL;
110     }
111     if (form->currentData) {
112         fastFree(form->currentData);
113         form->currentData = 0;
114     }
115 }
116
117 static void scheduleWithPair(const void* value, void* context)
118 {
119     const SchedulePair* pair = static_cast<const SchedulePair*>(value);
120     CFReadStreamRef stream = (CFReadStreamRef)context;
121
122     CFReadStreamScheduleWithRunLoop(stream, pair->runLoop, pair->mode);
123 }
124
125 static void advanceCurrentStream(FormStreamFields *form)
126 {
127     closeCurrentStream(form);
128
129     if (form->remainingElements.isEmpty())
130         return;
131
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;
139     } else {
140         CFStringRef filename = nextInput.m_filename.createCFString();
141         CFURLRef fileURL = CFURLCreateWithFileSystemPath(0, filename, kCFURLPOSIXPathStyle, FALSE);
142         CFRelease(filename);
143         form->currentStream = CFReadStreamCreateWithFile(0, fileURL);
144         CFRelease(fileURL);
145     }
146     form->remainingElements.removeLast();
147
148     // Set up the callback.
149     CFStreamClientContext context = { 0, form, NULL, NULL, NULL };
150     CFReadStreamSetClient(form->currentStream, kCFStreamEventHasBytesAvailable | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered,
151         formEventCallback, &context);
152
153     // Schedule with the current set of run loops.
154     CFSetApplyFunction(form->scheduledRunLoopPairs, scheduleWithPair, form->currentStream);
155 }
156
157 static void openNextStream(FormStreamFields* form)
158 {
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);
165 }
166
167 static void* formCreate(CFReadStreamRef stream, void* context)
168 {
169     FormData* formData = static_cast<FormData*>(context);
170
171     CFSetCallBacks runLoopAndModeCallBacks = { 0, pairRetain, pairRelease, NULL, pairEqual, pairHash };
172
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.
178
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]);
184
185     getStreamFormDatas().set(stream, adoptRef(formData));
186
187     return newInfo;
188 }
189
190 static void formFinalize(CFReadStreamRef stream, void* context)
191 {
192     FormStreamFields* form = static_cast<FormStreamFields*>(context);
193
194     getStreamFormDatas().remove(stream);
195
196     closeCurrentStream(form);
197     CFRelease(form->scheduledRunLoopPairs);
198     delete form;
199 }
200
201 static Boolean formOpen(CFReadStreamRef stream, CFStreamError* error, Boolean* openComplete, void* context)
202 {
203     FormStreamFields* form = static_cast<FormStreamFields*>(context);
204
205     openNextStream(form);
206
207     *openComplete = TRUE;
208     error->error = 0;
209     return TRUE;
210 }
211
212 static CFIndex formRead(CFReadStreamRef stream, UInt8* buffer, CFIndex bufferLength, CFStreamError* error, Boolean* atEOF, void* context)
213 {
214     FormStreamFields* form = static_cast<FormStreamFields*>(context);
215
216     while (form->currentStream) {
217         CFIndex bytesRead = CFReadStreamRead(form->currentStream, buffer, bufferLength);
218         if (bytesRead < 0) {
219             *error = CFReadStreamGetError(form->currentStream);
220             return -1;
221         }
222         if (bytesRead > 0) {
223             error->error = 0;
224             *atEOF = FALSE;
225             return bytesRead;
226         }
227         openNextStream(form);
228     }
229
230     error->error = 0;
231     *atEOF = TRUE;
232     return 0;
233 }
234
235 static Boolean formCanRead(CFReadStreamRef stream, void* context)
236 {
237     FormStreamFields* form = static_cast<FormStreamFields*>(context);
238
239     while (form->currentStream && CFReadStreamGetStatus(form->currentStream) == kCFStreamStatusAtEnd) {
240         openNextStream(form);
241     }
242     if (!form->currentStream) {
243         CFReadStreamSignalEvent(stream, kCFStreamEventEndEncountered, 0);
244         return FALSE;
245     }
246     return CFReadStreamHasBytesAvailable(form->currentStream);
247 }
248
249 static void formClose(CFReadStreamRef stream, void* context)
250 {
251     FormStreamFields* form = static_cast<FormStreamFields*>(context);
252
253     closeCurrentStream(form);
254 }
255
256 static void formSchedule(CFReadStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void* context)
257 {
258     FormStreamFields* form = static_cast<FormStreamFields*>(context);
259
260     if (form->currentStream)
261         CFReadStreamScheduleWithRunLoop(form->currentStream, runLoop, runLoopMode);
262     SchedulePair pair = { runLoop, runLoopMode };
263     CFSetAddValue(form->scheduledRunLoopPairs, &pair);
264 }
265
266 static void formUnschedule(CFReadStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void* context)
267 {
268     FormStreamFields* form = static_cast<FormStreamFields*>(context);
269
270     if (form->currentStream)
271         CFReadStreamUnscheduleFromRunLoop(form->currentStream, runLoop, runLoopMode);
272     SchedulePair pair = { runLoop, runLoopMode };
273     CFSetRemoveValue(form->scheduledRunLoopPairs, &pair);
274 }
275
276 static void formEventCallback(CFReadStreamRef stream, CFStreamEventType type, void* context)
277 {
278     FormStreamFields* form = static_cast<FormStreamFields*>(context);
279
280     switch (type) {
281     case kCFStreamEventHasBytesAvailable:
282         CFReadStreamSignalEvent(form->formStream, kCFStreamEventHasBytesAvailable, 0);
283         break;
284     case kCFStreamEventErrorOccurred: {
285         CFStreamError readStreamError = CFReadStreamGetError(stream);
286         CFReadStreamSignalEvent(form->formStream, kCFStreamEventErrorOccurred, &readStreamError);
287         break;
288     }
289     case kCFStreamEventEndEncountered:
290         openNextStream(form);
291         if (!form->currentStream)
292             CFReadStreamSignalEvent(form->formStream, kCFStreamEventEndEncountered, 0);
293         break;
294     case kCFStreamEventNone:
295         LOG_ERROR("unexpected kCFStreamEventNone");
296         break;
297     case kCFStreamEventOpenCompleted:
298         LOG_ERROR("unexpected kCFStreamEventOpenCompleted");
299         break;
300     case kCFStreamEventCanAcceptBytes:
301         LOG_ERROR("unexpected kCFStreamEventCanAcceptBytes");
302         break;
303     }
304 }
305
306 void setHTTPBody(CFMutableURLRequestRef request, PassRefPtr<FormData> formData)
307 {
308     size_t count = formData->elements().size();
309
310     if (count == 0)
311         return;
312
313     // Handle the common special case of one piece of form data, with no files.
314     if (count == 1) {
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);
319             CFRelease(data);
320             return;
321         }
322     }
323
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();
331         else {
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;
336             else
337                 haveLength = false;
338         }
339     }
340
341     if (haveLength) {
342         CFStringRef lengthStr = CFStringCreateWithFormat(0, 0, CFSTR("%lld"), length);
343         CFURLRequestSetHTTPHeaderFieldValue(request, CFSTR("Content-Length"), lengthStr);
344         CFRelease(lengthStr);
345     }
346
347     static CFReadStreamCallBacks formDataStreamCallbacks = 
348         { 1, formCreate, formFinalize, 0, formOpen, 0, formRead, 0, formCanRead, formClose, 0, 0, 0, formSchedule, formUnschedule};
349
350     CFReadStreamRef stream = CFReadStreamCreate(0, &formDataStreamCallbacks, formData.releaseRef());
351     CFURLRequestSetHTTPRequestBodyStream(request, stream);
352     CFRelease(stream);
353 }
354
355
356 FormData* httpBodyFromStream(CFReadStreamRef stream)
357 {
358     return getStreamFormDatas().get(stream).get();
359 }
360
361 }