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