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