97a1a441a2f1b0883f45bfd1d09a349ba57ac7c5
[WebKit-https.git] / WebCore / loader / mac / FormDataStream.m
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 "FormDataStream.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, const FormData*>* streamFormDatas = 0;
45
46 static HashMap<CFReadStreamRef, const FormData*>* getStreamFormDatas()
47 {
48     if (!streamFormDatas)
49         streamFormDatas = new HashMap<CFReadStreamRef, const FormData*>();
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 NSURLConnection
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     const FormData* formData = static_cast<const 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, new FormData(*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     delete getStreamFormDatas()->get(stream);
195     getStreamFormDatas()->remove(stream);
196
197     closeCurrentStream(form);
198     CFRelease(form->scheduledRunLoopPairs);
199     delete form;
200 }
201
202 static Boolean formOpen(CFReadStreamRef stream, CFStreamError* error, Boolean* openComplete, void* context)
203 {
204     FormStreamFields* form = static_cast<FormStreamFields*>(context);
205
206     openNextStream(form);
207
208     *openComplete = TRUE;
209     error->error = 0;
210     return TRUE;
211 }
212
213 static CFIndex formRead(CFReadStreamRef stream, UInt8* buffer, CFIndex bufferLength, CFStreamError* error, Boolean* atEOF, void* context)
214 {
215     FormStreamFields* form = static_cast<FormStreamFields*>(context);
216
217     while (form->currentStream) {
218         CFIndex bytesRead = CFReadStreamRead(form->currentStream, buffer, bufferLength);
219         if (bytesRead < 0) {
220             *error = CFReadStreamGetError(form->currentStream);
221             return -1;
222         }
223         if (bytesRead > 0) {
224             error->error = 0;
225             *atEOF = FALSE;
226             return bytesRead;
227         }
228         openNextStream(form);
229     }
230
231     error->error = 0;
232     *atEOF = TRUE;
233     return 0;
234 }
235
236 static Boolean formCanRead(CFReadStreamRef stream, void* context)
237 {
238     FormStreamFields* form = static_cast<FormStreamFields*>(context);
239
240     while (form->currentStream && CFReadStreamGetStatus(form->currentStream) == kCFStreamStatusAtEnd) {
241         openNextStream(form);
242     }
243     if (!form->currentStream) {
244         wkSignalCFReadStreamEnd(stream);
245         return FALSE;
246     }
247     return CFReadStreamHasBytesAvailable(form->currentStream);
248 }
249
250 static void formClose(CFReadStreamRef stream, void* context)
251 {
252     FormStreamFields* form = static_cast<FormStreamFields*>(context);
253
254     closeCurrentStream(form);
255 }
256
257 static void formSchedule(CFReadStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void* context)
258 {
259     FormStreamFields* form = static_cast<FormStreamFields*>(context);
260
261     if (form->currentStream)
262         CFReadStreamScheduleWithRunLoop(form->currentStream, runLoop, runLoopMode);
263     SchedulePair pair = { runLoop, runLoopMode };
264     CFSetAddValue(form->scheduledRunLoopPairs, &pair);
265 }
266
267 static void formUnschedule(CFReadStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void* context)
268 {
269     FormStreamFields* form = static_cast<FormStreamFields*>(context);
270
271     if (form->currentStream)
272         CFReadStreamUnscheduleFromRunLoop(form->currentStream, runLoop, runLoopMode);
273     SchedulePair pair = { runLoop, runLoopMode };
274     CFSetRemoveValue(form->scheduledRunLoopPairs, &pair);
275 }
276
277 static void formEventCallback(CFReadStreamRef stream, CFStreamEventType type, void* context)
278 {
279     FormStreamFields* form = static_cast<FormStreamFields*>(context);
280
281     switch (type) {
282     case kCFStreamEventHasBytesAvailable:
283         wkSignalCFReadStreamHasBytes(form->formStream);
284         break;
285     case kCFStreamEventErrorOccurred: {
286         CFStreamError readStreamError = CFReadStreamGetError(stream);
287         wkSignalCFReadStreamError(form->formStream, &readStreamError);
288         break;
289     }
290     case kCFStreamEventEndEncountered:
291         openNextStream(form);
292         if (!form->currentStream) {
293             wkSignalCFReadStreamEnd(form->formStream);
294         }
295         break;
296     case kCFStreamEventNone:
297         LOG_ERROR("unexpected kCFStreamEventNone");
298         break;
299     case kCFStreamEventOpenCompleted:
300         LOG_ERROR("unexpected kCFStreamEventOpenCompleted");
301         break;
302     case kCFStreamEventCanAcceptBytes:
303         LOG_ERROR("unexpected kCFStreamEventCanAcceptBytes");
304         break;
305     }
306 }
307
308 void setHTTPBody(NSMutableURLRequest *request, const FormData& formData)
309 {
310     size_t count = formData.elements().size();
311
312     // Handle the common special case of one piece of form data, with no files.
313     if (count == 1) {
314         const FormDataElement& element = formData.elements()[0];
315         if (element.m_type == FormDataElement::data) {
316             NSData *data = [[NSData alloc] initWithBytes:element.m_data.data() length:element.m_data.size()];
317             [request setHTTPBody:data];
318             [data release];
319             return;
320         }
321     }
322
323     // Precompute the content length so NSURLConnection doesn't use chunked mode.
324     long long length = 0;
325     for (size_t i = 0; i < count; ++i) {
326         const FormDataElement& element = formData.elements()[i];
327         if (element.m_type == FormDataElement::data)
328             length += element.m_data.size();
329         else {
330             struct stat sb;
331             int statResult = stat(element.m_filename.utf8(), &sb);
332             if (statResult == 0 && (sb.st_mode & S_IFMT) == S_IFREG)
333                 length += sb.st_size;
334         }
335     }
336
337     // Set the length.
338     [request setValue:[NSString stringWithFormat:@"%lld", length] forHTTPHeaderField:@"Content-Length"];
339
340     // Create and set the stream.
341     CFReadStreamRef stream = wkCreateCustomCFReadStream(formCreate, formFinalize,
342         formOpen, formRead, formCanRead, formClose, formSchedule, formUnschedule,
343         const_cast<FormData*>(&formData));
344     [request setHTTPBodyStream:(NSInputStream *)stream];
345     CFRelease(stream);
346 }
347
348
349 const FormData* httpBodyFromStream(NSInputStream* stream)
350 {
351     return getStreamFormDatas()->get((CFReadStreamRef)stream);
352 }
353
354 }