8aa9a6df6c8be7876a5fefd478a94d8519d00dce
[WebKit-https.git] / WebCore / platform / network / mac / FormDataStreamMac.mm
1 /*
2  * Copyright (C) 2005, 2006, 2008 Apple 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 "FileSystem.h"
36 #import "FormData.h"
37 #import "ResourceHandle.h"
38 #import "ResourceHandleClient.h"
39 #import "SchedulePair.h"
40 #import "WebCoreSystemInterface.h"
41 #import <sys/stat.h>
42 #import <sys/types.h>
43 #import <wtf/Assertions.h>
44 #import <wtf/HashMap.h>
45 #import <wtf/MainThread.h>
46 #import <wtf/StdLibExtras.h>
47 #import <wtf/Threading.h>
48
49 namespace WebCore {
50
51 typedef HashMap<CFReadStreamRef, RefPtr<FormData> > StreamFormDataMap;
52 static StreamFormDataMap& getStreamFormDataMap()
53 {
54     DEFINE_STATIC_LOCAL(StreamFormDataMap, streamFormDataMap, ());
55     return streamFormDataMap;
56 }
57
58 typedef HashMap<CFReadStreamRef, RefPtr<ResourceHandle> > StreamResourceHandleMap;
59 static StreamResourceHandleMap& getStreamResourceHandleMap()
60 {
61     DEFINE_STATIC_LOCAL(StreamResourceHandleMap, streamResourceHandleMap, ());
62     return streamResourceHandleMap;
63 }
64
65 void associateStreamWithResourceHandle(NSInputStream *stream, ResourceHandle* resourceHandle)
66 {
67     ASSERT(isMainThread());
68
69     ASSERT(resourceHandle);
70
71     if (!stream)
72         return;
73
74     if (!getStreamFormDataMap().contains((CFReadStreamRef)stream))
75         return;
76
77     ASSERT(!getStreamResourceHandleMap().contains((CFReadStreamRef)stream));
78     getStreamResourceHandleMap().set((CFReadStreamRef)stream, resourceHandle);
79 }
80
81 void disassociateStreamWithResourceHandle(NSInputStream *stream)
82 {
83     ASSERT(isMainThread());
84
85     if (!stream)
86         return;
87
88     getStreamResourceHandleMap().remove((CFReadStreamRef)stream);
89 }
90
91 struct DidSendDataCallbackData {
92     DidSendDataCallbackData(CFReadStreamRef stream_, unsigned long long bytesSent_, unsigned long long streamLength_)
93         : stream(stream_)
94         , bytesSent(bytesSent_)
95         , streamLength(streamLength_)
96     {
97     }
98
99     CFReadStreamRef stream;
100     unsigned long long bytesSent;
101     unsigned long long streamLength;
102 };
103
104 static void performDidSendDataCallback(void* context)
105 {
106     ASSERT(isMainThread());
107
108     DidSendDataCallbackData* data = static_cast<DidSendDataCallbackData*>(context);
109     ResourceHandle* resourceHandle = getStreamResourceHandleMap().get(data->stream).get();
110
111     if (resourceHandle && resourceHandle->client())
112         resourceHandle->client()->didSendData(resourceHandle, data->bytesSent, data->streamLength);
113
114     delete data;
115 }
116
117 static void formEventCallback(CFReadStreamRef stream, CFStreamEventType type, void* context);
118
119 struct FormContext {
120     FormData* formData;
121     unsigned long long streamLength;
122 };
123
124 struct FormStreamFields {
125     SchedulePairHashSet scheduledRunLoopPairs;
126     Vector<FormDataElement> remainingElements; // in reverse order
127     CFReadStreamRef currentStream;
128     char* currentData;
129     CFReadStreamRef formStream;
130     unsigned long long streamLength;
131     unsigned long long bytesSent;
132 };
133
134 static void closeCurrentStream(FormStreamFields *form)
135 {
136     if (form->currentStream) {
137         CFReadStreamClose(form->currentStream);
138         CFReadStreamSetClient(form->currentStream, kCFStreamEventNone, NULL, NULL);
139         CFRelease(form->currentStream);
140         form->currentStream = NULL;
141     }
142     if (form->currentData) {
143         fastFree(form->currentData);
144         form->currentData = 0;
145     }
146 }
147
148 static void advanceCurrentStream(FormStreamFields *form)
149 {
150     closeCurrentStream(form);
151
152     if (form->remainingElements.isEmpty())
153         return;
154
155     // Create the new stream.
156     FormDataElement& nextInput = form->remainingElements.last();
157     if (nextInput.m_type == FormDataElement::data) {
158         size_t size = nextInput.m_data.size();
159         char* data = nextInput.m_data.releaseBuffer();
160         form->currentStream = CFReadStreamCreateWithBytesNoCopy(0, reinterpret_cast<const UInt8*>(data), size, kCFAllocatorNull);
161         form->currentData = data;
162     } else {
163         const String& path = nextInput.m_shouldGenerateFile ? nextInput.m_generatedFilename : nextInput.m_filename;
164         RetainPtr<CFStringRef> filename(AdoptCF, path.createCFString());
165         RetainPtr<CFURLRef> fileURL(AdoptCF, CFURLCreateWithFileSystemPath(0, filename.get(), kCFURLPOSIXPathStyle, FALSE));
166         form->currentStream = CFReadStreamCreateWithFile(0, fileURL.get());
167     }
168     form->remainingElements.removeLast();
169
170     // Set up the callback.
171     CFStreamClientContext context = { 0, form, NULL, NULL, NULL };
172     CFReadStreamSetClient(form->currentStream, kCFStreamEventHasBytesAvailable | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered,
173         formEventCallback, &context);
174
175     // Schedule with the current set of run loops.
176     SchedulePairHashSet::iterator end = form->scheduledRunLoopPairs.end();
177     for (SchedulePairHashSet::iterator it = form->scheduledRunLoopPairs.begin(); it != end; ++it)
178         CFReadStreamScheduleWithRunLoop(form->currentStream, (*it)->runLoop(), (*it)->mode());
179 }
180
181 static void openNextStream(FormStreamFields* form)
182 {
183     // Skip over any streams we can't open.
184     // For some purposes we might want to return an error, but the current NSURLConnection
185     // can't really do anything useful with an error at this point, so this is better.
186     advanceCurrentStream(form);
187     while (form->currentStream && !CFReadStreamOpen(form->currentStream))
188         advanceCurrentStream(form);
189 }
190
191 static void* formCreate(CFReadStreamRef stream, void* context)
192 {
193     FormContext* formContext = static_cast<FormContext*>(context);
194
195     FormStreamFields* newInfo = new FormStreamFields;
196     newInfo->currentStream = NULL;
197     newInfo->currentData = 0;
198     newInfo->formStream = stream; // Don't retain. That would create a reference cycle.
199     newInfo->streamLength = formContext->streamLength;
200     newInfo->bytesSent = 0;
201
202     FormData* formData = formContext->formData;
203
204     // Append in reverse order since we remove elements from the end.
205     size_t size = formData->elements().size();
206     newInfo->remainingElements.reserveInitialCapacity(size);
207     for (size_t i = 0; i < size; ++i)
208         newInfo->remainingElements.append(formData->elements()[size - i - 1]);
209
210     getStreamFormDataMap().set(stream, adoptRef(formData));
211
212     return newInfo;
213 }
214
215 static void formFinalize(CFReadStreamRef stream, void* context)
216 {
217     FormStreamFields* form = static_cast<FormStreamFields*>(context);
218
219     getStreamFormDataMap().remove(stream);
220
221     closeCurrentStream(form);
222     delete form;
223 }
224
225 static Boolean formOpen(CFReadStreamRef, CFStreamError* error, Boolean* openComplete, void* context)
226 {
227     FormStreamFields* form = static_cast<FormStreamFields*>(context);
228
229     openNextStream(form);
230
231     *openComplete = TRUE;
232     error->error = 0;
233     return TRUE;
234 }
235
236 static CFIndex formRead(CFReadStreamRef stream, UInt8* buffer, CFIndex bufferLength, CFStreamError* error, Boolean* atEOF, void* context)
237 {
238     FormStreamFields* form = static_cast<FormStreamFields*>(context);
239
240     while (form->currentStream) {
241         CFIndex bytesRead = CFReadStreamRead(form->currentStream, buffer, bufferLength);
242         if (bytesRead < 0) {
243             *error = CFReadStreamGetError(form->currentStream);
244             return -1;
245         }
246         if (bytesRead > 0) {
247             error->error = 0;
248             *atEOF = FALSE;
249             form->bytesSent += bytesRead;
250
251             if (!ResourceHandle::didSendBodyDataDelegateExists()) {
252                 // FIXME: Figure out how to only do this when a ResourceHandleClient is available.
253                 DidSendDataCallbackData* data = new DidSendDataCallbackData(stream, form->bytesSent, form->streamLength);
254                 callOnMainThread(performDidSendDataCallback, data);
255             }
256
257             return bytesRead;
258         }
259         openNextStream(form);
260     }
261
262     error->error = 0;
263     *atEOF = TRUE;
264     return 0;
265 }
266
267 static Boolean formCanRead(CFReadStreamRef stream, void* context)
268 {
269     FormStreamFields* form = static_cast<FormStreamFields*>(context);
270
271     while (form->currentStream && CFReadStreamGetStatus(form->currentStream) == kCFStreamStatusAtEnd) {
272         openNextStream(form);
273     }
274     if (!form->currentStream) {
275         wkSignalCFReadStreamEnd(stream);
276         return FALSE;
277     }
278     return CFReadStreamHasBytesAvailable(form->currentStream);
279 }
280
281 static void formClose(CFReadStreamRef, void* context)
282 {
283     FormStreamFields* form = static_cast<FormStreamFields*>(context);
284
285     closeCurrentStream(form);
286 }
287
288 static void formSchedule(CFReadStreamRef, CFRunLoopRef runLoop, CFStringRef runLoopMode, void* context)
289 {
290     FormStreamFields* form = static_cast<FormStreamFields*>(context);
291
292     if (form->currentStream)
293         CFReadStreamScheduleWithRunLoop(form->currentStream, runLoop, runLoopMode);
294     form->scheduledRunLoopPairs.add(SchedulePair::create(runLoop, runLoopMode));
295 }
296
297 static void formUnschedule(CFReadStreamRef, CFRunLoopRef runLoop, CFStringRef runLoopMode, void* context)
298 {
299     FormStreamFields* form = static_cast<FormStreamFields*>(context);
300
301     if (form->currentStream)
302         CFReadStreamUnscheduleFromRunLoop(form->currentStream, runLoop, runLoopMode);
303     form->scheduledRunLoopPairs.remove(SchedulePair::create(runLoop, runLoopMode));
304 }
305
306 static void formEventCallback(CFReadStreamRef stream, CFStreamEventType type, void* context)
307 {
308     FormStreamFields* form = static_cast<FormStreamFields*>(context);
309
310     switch (type) {
311     case kCFStreamEventHasBytesAvailable:
312         wkSignalCFReadStreamHasBytes(form->formStream);
313         break;
314     case kCFStreamEventErrorOccurred: {
315         CFStreamError readStreamError = CFReadStreamGetError(stream);
316         wkSignalCFReadStreamError(form->formStream, &readStreamError);
317         break;
318     }
319     case kCFStreamEventEndEncountered:
320         openNextStream(form);
321         if (!form->currentStream) {
322             wkSignalCFReadStreamEnd(form->formStream);
323         }
324         break;
325     case kCFStreamEventNone:
326         LOG_ERROR("unexpected kCFStreamEventNone");
327         break;
328     case kCFStreamEventOpenCompleted:
329         LOG_ERROR("unexpected kCFStreamEventOpenCompleted");
330         break;
331     case kCFStreamEventCanAcceptBytes:
332         LOG_ERROR("unexpected kCFStreamEventCanAcceptBytes");
333         break;
334     }
335 }
336
337 void setHTTPBody(NSMutableURLRequest *request, PassRefPtr<FormData> formData)
338 {
339     if (!formData)
340         return;
341         
342     size_t count = formData->elements().size();
343
344     // Handle the common special case of one piece of form data, with no files.
345     if (count == 1 && !formData->alwaysStream()) {
346         const FormDataElement& element = formData->elements()[0];
347         if (element.m_type == FormDataElement::data) {
348             NSData *data = [[NSData alloc] initWithBytes:element.m_data.data() length:element.m_data.size()];
349             [request setHTTPBody:data];
350             [data release];
351             return;
352         }
353     }
354
355     // Precompute the content length so NSURLConnection doesn't use chunked mode.
356     long long length = 0;
357     for (size_t i = 0; i < count; ++i) {
358         const FormDataElement& element = formData->elements()[i];
359         if (element.m_type == FormDataElement::data)
360             length += element.m_data.size();
361         else {
362             long long fileSize;
363             if (getFileSize(element.m_shouldGenerateFile ? element.m_generatedFilename : element.m_filename, fileSize))
364                 length += fileSize;
365         }
366     }
367
368     // Set the length.
369     [request setValue:[NSString stringWithFormat:@"%lld", length] forHTTPHeaderField:@"Content-Length"];
370
371     // Create and set the stream.
372
373     // Pass the length along with the formData so it does not have to be recomputed.
374     FormContext formContext = { formData.releaseRef(), length };
375
376     RetainPtr<CFReadStreamRef> stream(AdoptCF, wkCreateCustomCFReadStream(formCreate, formFinalize,
377         formOpen, formRead, formCanRead, formClose, formSchedule, formUnschedule,
378         &formContext));
379     [request setHTTPBodyStream:(NSInputStream *)stream.get()];
380 }
381
382 FormData* httpBodyFromStream(NSInputStream* stream)
383 {
384     return getStreamFormDataMap().get((CFReadStreamRef)stream).get();
385 }
386
387 } // namespace WebCore