Reviewed by Darin.
[WebKit-https.git] / WebCore / platform / network / mac / FormDataStreamMac.mm
1 /*
2  * Copyright (C) 2005, 2006 Apple Computer, Inc.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
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. 
16  *
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.
27  */
28
29 /* originally written by Becky Willrich, additional code by Darin Adler */
30
31 #import "config.h"
32 #import "FormDataStreamMac.h"
33
34 #import "CString.h"
35 #import "FormData.h"
36 #import "WebCoreSystemInterface.h"
37 #import <sys/stat.h>
38 #import <sys/types.h>
39 #import <wtf/Assertions.h>
40 #import <wtf/HashMap.h>
41
42 namespace WebCore {
43
44 static HashMap<CFReadStreamRef, RefPtr<FormData> >& getStreamFormDatas()
45 {
46     static HashMap<CFReadStreamRef, RefPtr<FormData> > streamFormDatas;
47     return streamFormDatas;
48 }
49
50 static void formEventCallback(CFReadStreamRef stream, CFStreamEventType type, void* context);
51
52 struct FormStreamFields {
53     CFMutableSetRef scheduledRunLoopPairs;
54     Vector<FormDataElement> remainingElements; // in reverse order
55     CFReadStreamRef currentStream;
56     char* currentData;
57     CFReadStreamRef formStream;
58 };
59
60 struct SchedulePair {
61     CFRunLoopRef runLoop;
62     CFStringRef mode;
63 };
64
65 static const void* pairRetain(CFAllocatorRef alloc, const void* value)
66 {
67     const SchedulePair* pair = static_cast<const SchedulePair*>(value);
68
69     SchedulePair* result = new SchedulePair;
70     CFRetain(pair->runLoop);
71     result->runLoop = pair->runLoop;
72     result->mode = CFStringCreateCopy(alloc, pair->mode);
73     return result;
74 }
75
76 static void pairRelease(CFAllocatorRef alloc, const void* value)
77 {
78     const SchedulePair* pair = static_cast<const SchedulePair*>(value);
79
80     CFRelease(pair->runLoop);
81     CFRelease(pair->mode);
82     delete pair;
83 }
84
85 static Boolean pairEqual(const void* a, const void* b)
86 {
87     const SchedulePair* pairA = static_cast<const SchedulePair*>(a);
88     const SchedulePair* pairB = static_cast<const SchedulePair*>(b);
89
90     return pairA->runLoop == pairB->runLoop && CFEqual(pairA->mode, pairB->mode);
91 }
92
93 static CFHashCode pairHash(const void* value)
94 {
95     const SchedulePair* pair = static_cast<const SchedulePair*>(value);
96
97     return (CFHashCode)pair->runLoop ^ CFHash(pair->mode);
98 }
99
100 static void closeCurrentStream(FormStreamFields *form)
101 {
102     if (form->currentStream) {
103         CFReadStreamClose(form->currentStream);
104         CFReadStreamSetClient(form->currentStream, kCFStreamEventNone, NULL, NULL);
105         CFRelease(form->currentStream);
106         form->currentStream = NULL;
107     }
108     if (form->currentData) {
109         fastFree(form->currentData);
110         form->currentData = 0;
111     }
112 }
113
114 static void scheduleWithPair(const void* value, void* context)
115 {
116     const SchedulePair* pair = static_cast<const SchedulePair*>(value);
117     CFReadStreamRef stream = (CFReadStreamRef)context;
118
119     CFReadStreamScheduleWithRunLoop(stream, pair->runLoop, pair->mode);
120 }
121
122 static void advanceCurrentStream(FormStreamFields *form)
123 {
124     closeCurrentStream(form);
125
126     if (form->remainingElements.isEmpty())
127         return;
128
129     // Create the new stream.
130     FormDataElement& nextInput = form->remainingElements.last();
131     if (nextInput.m_type == FormDataElement::data) {
132         size_t size = nextInput.m_data.size();
133         char* data = nextInput.m_data.releaseBuffer();
134         form->currentStream = CFReadStreamCreateWithBytesNoCopy(0, reinterpret_cast<const UInt8*>(data), size, kCFAllocatorNull);
135         form->currentData = data;
136     } else {
137         CFStringRef filename = nextInput.m_filename.createCFString();
138         CFURLRef fileURL = CFURLCreateWithFileSystemPath(0, filename, kCFURLPOSIXPathStyle, FALSE);
139         CFRelease(filename);
140         form->currentStream = CFReadStreamCreateWithFile(0, fileURL);
141         CFRelease(fileURL);
142     }
143     form->remainingElements.removeLast();
144
145     // Set up the callback.
146     CFStreamClientContext context = { 0, form, NULL, NULL, NULL };
147     CFReadStreamSetClient(form->currentStream, kCFStreamEventHasBytesAvailable | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered,
148         formEventCallback, &context);
149
150     // Schedule with the current set of run loops.
151     CFSetApplyFunction(form->scheduledRunLoopPairs, scheduleWithPair, form->currentStream);
152 }
153
154 static void openNextStream(FormStreamFields* form)
155 {
156     // Skip over any streams we can't open.
157     // For some purposes we might want to return an error, but the current NSURLConnection
158     // can't really do anything useful with an error at this point, so this is better.
159     advanceCurrentStream(form);
160     while (form->currentStream && !CFReadStreamOpen(form->currentStream))
161         advanceCurrentStream(form);
162 }
163
164 static void* formCreate(CFReadStreamRef stream, void* context)
165 {
166     FormData* formData = static_cast<FormData*>(context);
167
168     CFSetCallBacks runLoopAndModeCallBacks = { 0, pairRetain, pairRelease, NULL, pairEqual, pairHash };
169
170     FormStreamFields* newInfo = new FormStreamFields;
171     newInfo->scheduledRunLoopPairs = CFSetCreateMutable(0, 0, &runLoopAndModeCallBacks);
172     newInfo->currentStream = NULL;
173     newInfo->currentData = 0;
174     newInfo->formStream = stream; // Don't retain. That would create a reference cycle.
175
176     // Append in reverse order since we remove elements from the end.
177     size_t size = formData->elements().size();
178     newInfo->remainingElements.reserveCapacity(size);
179     for (size_t i = 0; i < size; ++i)
180         newInfo->remainingElements.append(formData->elements()[size - i - 1]);
181
182     getStreamFormDatas().set(stream, adoptRef(formData));
183
184     return newInfo;
185 }
186
187 static void formFinalize(CFReadStreamRef stream, void* context)
188 {
189     FormStreamFields* form = static_cast<FormStreamFields*>(context);
190
191     getStreamFormDatas().remove(stream);
192
193     closeCurrentStream(form);
194     CFRelease(form->scheduledRunLoopPairs);
195     delete form;
196 }
197
198 static Boolean formOpen(CFReadStreamRef stream, CFStreamError* error, Boolean* openComplete, void* context)
199 {
200     FormStreamFields* form = static_cast<FormStreamFields*>(context);
201
202     openNextStream(form);
203
204     *openComplete = TRUE;
205     error->error = 0;
206     return TRUE;
207 }
208
209 static CFIndex formRead(CFReadStreamRef stream, UInt8* buffer, CFIndex bufferLength, CFStreamError* error, Boolean* atEOF, void* context)
210 {
211     FormStreamFields* form = static_cast<FormStreamFields*>(context);
212
213     while (form->currentStream) {
214         CFIndex bytesRead = CFReadStreamRead(form->currentStream, buffer, bufferLength);
215         if (bytesRead < 0) {
216             *error = CFReadStreamGetError(form->currentStream);
217             return -1;
218         }
219         if (bytesRead > 0) {
220             error->error = 0;
221             *atEOF = FALSE;
222             return bytesRead;
223         }
224         openNextStream(form);
225     }
226
227     error->error = 0;
228     *atEOF = TRUE;
229     return 0;
230 }
231
232 static Boolean formCanRead(CFReadStreamRef stream, void* context)
233 {
234     FormStreamFields* form = static_cast<FormStreamFields*>(context);
235
236     while (form->currentStream && CFReadStreamGetStatus(form->currentStream) == kCFStreamStatusAtEnd) {
237         openNextStream(form);
238     }
239     if (!form->currentStream) {
240         wkSignalCFReadStreamEnd(stream);
241         return FALSE;
242     }
243     return CFReadStreamHasBytesAvailable(form->currentStream);
244 }
245
246 static void formClose(CFReadStreamRef stream, void* context)
247 {
248     FormStreamFields* form = static_cast<FormStreamFields*>(context);
249
250     closeCurrentStream(form);
251 }
252
253 static void formSchedule(CFReadStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void* context)
254 {
255     FormStreamFields* form = static_cast<FormStreamFields*>(context);
256
257     if (form->currentStream)
258         CFReadStreamScheduleWithRunLoop(form->currentStream, runLoop, runLoopMode);
259     SchedulePair pair = { runLoop, runLoopMode };
260     CFSetAddValue(form->scheduledRunLoopPairs, &pair);
261 }
262
263 static void formUnschedule(CFReadStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void* context)
264 {
265     FormStreamFields* form = static_cast<FormStreamFields*>(context);
266
267     if (form->currentStream)
268         CFReadStreamUnscheduleFromRunLoop(form->currentStream, runLoop, runLoopMode);
269     SchedulePair pair = { runLoop, runLoopMode };
270     CFSetRemoveValue(form->scheduledRunLoopPairs, &pair);
271 }
272
273 static void formEventCallback(CFReadStreamRef stream, CFStreamEventType type, void* context)
274 {
275     FormStreamFields* form = static_cast<FormStreamFields*>(context);
276
277     switch (type) {
278     case kCFStreamEventHasBytesAvailable:
279         wkSignalCFReadStreamHasBytes(form->formStream);
280         break;
281     case kCFStreamEventErrorOccurred: {
282         CFStreamError readStreamError = CFReadStreamGetError(stream);
283         wkSignalCFReadStreamError(form->formStream, &readStreamError);
284         break;
285     }
286     case kCFStreamEventEndEncountered:
287         openNextStream(form);
288         if (!form->currentStream) {
289             wkSignalCFReadStreamEnd(form->formStream);
290         }
291         break;
292     case kCFStreamEventNone:
293         LOG_ERROR("unexpected kCFStreamEventNone");
294         break;
295     case kCFStreamEventOpenCompleted:
296         LOG_ERROR("unexpected kCFStreamEventOpenCompleted");
297         break;
298     case kCFStreamEventCanAcceptBytes:
299         LOG_ERROR("unexpected kCFStreamEventCanAcceptBytes");
300         break;
301     }
302 }
303
304 void setHTTPBody(NSMutableURLRequest *request, PassRefPtr<FormData> formData)
305 {
306     if (!formData)
307         return;
308         
309     size_t count = formData->elements().size();
310
311     // Handle the common special case of one piece of form data, with no files.
312     if (count == 1) {
313         const FormDataElement& element = formData->elements()[0];
314         if (element.m_type == FormDataElement::data) {
315             NSData *data = [[NSData alloc] initWithBytes:element.m_data.data() length:element.m_data.size()];
316             [request setHTTPBody:data];
317             [data release];
318             return;
319         }
320     }
321
322     // Precompute the content length so NSURLConnection doesn't use chunked mode.
323     long long length = 0;
324     for (size_t i = 0; i < count; ++i) {
325         const FormDataElement& element = formData->elements()[i];
326         if (element.m_type == FormDataElement::data)
327             length += element.m_data.size();
328         else {
329             struct stat sb;
330             int statResult = stat(element.m_filename.utf8(), &sb);
331             if (statResult == 0 && (sb.st_mode & S_IFMT) == S_IFREG)
332                 length += sb.st_size;
333         }
334     }
335
336     // Set the length.
337     [request setValue:[NSString stringWithFormat:@"%lld", length] forHTTPHeaderField:@"Content-Length"];
338
339     // Create and set the stream.
340     CFReadStreamRef stream = wkCreateCustomCFReadStream(formCreate, formFinalize,
341         formOpen, formRead, formCanRead, formClose, formSchedule, formUnschedule,
342         formData.releaseRef());
343     [request setHTTPBodyStream:(NSInputStream *)stream];
344     CFRelease(stream);
345 }
346
347
348 FormData* httpBodyFromStream(NSInputStream* stream)
349 {
350     return getStreamFormDatas().get((CFReadStreamRef)stream).get();
351 }
352
353 }