Add WTF::move()
[WebKit-https.git] / Source / WebCore / platform / network / cf / FormDataStreamCFNet.cpp
1 /*
2  * Copyright (C) 2005, 2006, 2007, 2012 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 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 #include "config.h"
30 #include "FormDataStreamCFNet.h"
31
32 #include "BlobData.h"
33 #include "FileSystem.h"
34 #include "FormData.h"
35 #include <sys/stat.h>
36 #include <sys/types.h>
37 #include <wtf/Assertions.h>
38 #include <wtf/HashMap.h>
39 #include <wtf/MainThread.h>
40 #include <wtf/RetainPtr.h>
41 #include <wtf/SchedulePair.h>
42 #include <wtf/StdLibExtras.h>
43 #include <wtf/Threading.h>
44
45 #if PLATFORM(IOS)
46 #include <MacErrors.h>
47 #elif PLATFORM(MAC)
48 #include <CoreServices/CoreServices.h>
49 #endif
50
51 #if PLATFORM(COCOA)
52 extern "C" void CFURLRequestSetHTTPRequestBody(CFMutableURLRequestRef mutableHTTPRequest, CFDataRef httpBody);
53 extern "C" void CFURLRequestSetHTTPHeaderFieldValue(CFMutableURLRequestRef mutableHTTPRequest, CFStringRef httpHeaderField, CFStringRef httpHeaderFieldValue);
54 extern "C" void CFURLRequestSetHTTPRequestBodyStream(CFMutableURLRequestRef req, CFReadStreamRef bodyStream);
55 #elif PLATFORM(WIN)
56 #include <CFNetwork/CFURLRequest.h>
57 #endif
58
59 typedef struct {
60     CFIndex version; /* == 1 */
61     void *(*create)(CFReadStreamRef stream, void *info);
62     void (*finalize)(CFReadStreamRef stream, void *info);
63     CFStringRef (*copyDescription)(CFReadStreamRef stream, void *info);
64     Boolean (*open)(CFReadStreamRef stream, CFStreamError *error, Boolean *openComplete, void *info);
65     Boolean (*openCompleted)(CFReadStreamRef stream, CFStreamError *error, void *info);
66     CFIndex (*read)(CFReadStreamRef stream, UInt8 *buffer, CFIndex bufferLength, CFStreamError *error, Boolean *atEOF, void *info);
67     const UInt8 *(*getBuffer)(CFReadStreamRef stream, CFIndex maxBytesToRead, CFIndex *numBytesRead, CFStreamError *error, Boolean *atEOF, void *info);
68     Boolean (*canRead)(CFReadStreamRef stream, void *info);
69     void (*close)(CFReadStreamRef stream, void *info);
70     CFTypeRef (*copyProperty)(CFReadStreamRef stream, CFStringRef propertyName, void *info);
71     Boolean (*setProperty)(CFReadStreamRef stream, CFStringRef propertyName, CFTypeRef propertyValue, void *info);
72     void (*requestEvents)(CFReadStreamRef stream, CFOptionFlags streamEvents, void *info);
73     void (*schedule)(CFReadStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info);
74     void (*unschedule)(CFReadStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info);
75 } CFReadStreamCallBacksV1;
76
77 #if PLATFORM(WIN)
78 #define EXTERN extern "C" __declspec(dllimport)
79 #else
80 #define EXTERN extern "C"
81 #endif
82
83 EXTERN void CFReadStreamSignalEvent(CFReadStreamRef stream, CFStreamEventType event, const void *error);
84 EXTERN CFReadStreamRef CFReadStreamCreate(CFAllocatorRef alloc, const void *callbacks, void *info);
85
86 namespace WebCore {
87
88 static void formEventCallback(CFReadStreamRef stream, CFStreamEventType type, void* context);
89
90 static CFStringRef formDataPointerPropertyName = CFSTR("WebKitFormDataPointer");
91
92 CFStringRef formDataStreamLengthPropertyName()
93 {
94     return CFSTR("WebKitFormDataStreamLength");
95 }
96
97 struct FormCreationContext {
98     RefPtr<FormData> formData;
99     unsigned long long streamLength;
100 };
101
102 struct FormStreamFields {
103     RefPtr<FormData> formData;
104     SchedulePairHashSet scheduledRunLoopPairs;
105     Vector<FormDataElement> remainingElements; // in reverse order
106     CFReadStreamRef currentStream;
107     long long currentStreamRangeLength;
108     MallocPtr<char> currentData;
109     CFReadStreamRef formStream;
110     unsigned long long streamLength;
111     unsigned long long bytesSent;
112 };
113
114 static void closeCurrentStream(FormStreamFields* form)
115 {
116     if (form->currentStream) {
117         CFReadStreamClose(form->currentStream);
118         CFReadStreamSetClient(form->currentStream, kCFStreamEventNone, 0, 0);
119         CFRelease(form->currentStream);
120         form->currentStream = 0;
121         form->currentStreamRangeLength = BlobDataItem::toEndOfFile;
122     }
123
124     form->currentData = nullptr;
125 }
126
127 // 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.
128 static bool advanceCurrentStream(FormStreamFields* form)
129 {
130     closeCurrentStream(form);
131
132     if (form->remainingElements.isEmpty())
133         return true;
134
135     // Create the new stream.
136     FormDataElement& nextInput = form->remainingElements.last();
137
138     if (nextInput.m_type == FormDataElement::Type::Data) {
139         size_t size = nextInput.m_data.size();
140         MallocPtr<char> data = nextInput.m_data.releaseBuffer();
141         form->currentStream = CFReadStreamCreateWithBytesNoCopy(0, reinterpret_cast<const UInt8*>(data.get()), size, kCFAllocatorNull);
142         form->currentData = WTF::move(data);
143     } else {
144         // Check if the file has been changed or not if required.
145         if (isValidFileTime(nextInput.m_expectedFileModificationTime)) {
146             time_t fileModificationTime;
147             if (!getFileModificationTime(nextInput.m_filename, fileModificationTime) || fileModificationTime != static_cast<time_t>(nextInput.m_expectedFileModificationTime))
148                 return false;
149         }
150         const String& path = nextInput.m_shouldGenerateFile ? nextInput.m_generatedFilename : nextInput.m_filename;
151         form->currentStream = CFReadStreamCreateWithFile(0, pathAsURL(path).get());
152         if (!form->currentStream) {
153             // The file must have been removed or become unreadable.
154             return false;
155         }
156         if (nextInput.m_fileStart > 0) {
157             RetainPtr<CFNumberRef> position = adoptCF(CFNumberCreate(0, kCFNumberLongLongType, &nextInput.m_fileStart));
158             CFReadStreamSetProperty(form->currentStream, kCFStreamPropertyFileCurrentOffset, position.get());
159         }
160         form->currentStreamRangeLength = nextInput.m_fileLength;
161     }
162     form->remainingElements.removeLast();
163
164     // Set up the callback.
165     CFStreamClientContext context = { 0, form, 0, 0, 0 };
166     CFReadStreamSetClient(form->currentStream, kCFStreamEventHasBytesAvailable | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered,
167         formEventCallback, &context);
168
169     // Schedule with the current set of run loops.
170     SchedulePairHashSet::iterator end = form->scheduledRunLoopPairs.end();
171     for (SchedulePairHashSet::iterator it = form->scheduledRunLoopPairs.begin(); it != end; ++it)
172         CFReadStreamScheduleWithRunLoop(form->currentStream, (*it)->runLoop(), (*it)->mode());
173
174     return true;
175 }
176
177 static bool openNextStream(FormStreamFields* form)
178 {
179     // Skip over any streams we can't open.
180     if (!advanceCurrentStream(form))
181         return false;
182     while (form->currentStream && !CFReadStreamOpen(form->currentStream)) {
183         if (!advanceCurrentStream(form))
184             return false;
185     }
186     return true;
187 }
188
189 static void* formCreate(CFReadStreamRef stream, void* context)
190 {
191     FormCreationContext* formContext = static_cast<FormCreationContext*>(context);
192
193     FormStreamFields* newInfo = new FormStreamFields;
194     newInfo->formData = formContext->formData.release();
195     newInfo->currentStream = 0;
196     newInfo->currentStreamRangeLength = BlobDataItem::toEndOfFile;
197     newInfo->formStream = stream; // Don't retain. That would create a reference cycle.
198     newInfo->streamLength = formContext->streamLength;
199     newInfo->bytesSent = 0;
200
201     // Append in reverse order since we remove elements from the end.
202     size_t size = newInfo->formData->elements().size();
203     newInfo->remainingElements.reserveInitialCapacity(size);
204     for (size_t i = 0; i < size; ++i)
205         newInfo->remainingElements.append(newInfo->formData->elements()[size - i - 1]);
206
207     return newInfo;
208 }
209
210 static void formFinishFinalizationOnMainThread(void* context)
211 {
212     OwnPtr<FormStreamFields> form = adoptPtr(static_cast<FormStreamFields*>(context));
213
214     closeCurrentStream(form.get());
215 }
216
217 static void formFinalize(CFReadStreamRef stream, void* context)
218 {
219     FormStreamFields* form = static_cast<FormStreamFields*>(context);
220     ASSERT_UNUSED(stream, form->formStream == stream);
221
222     callOnMainThread(formFinishFinalizationOnMainThread, form);
223 }
224
225 static Boolean formOpen(CFReadStreamRef, CFStreamError* error, Boolean* openComplete, void* context)
226 {
227     FormStreamFields* form = static_cast<FormStreamFields*>(context);
228
229     bool opened = openNextStream(form);
230
231     *openComplete = opened;
232     error->error = opened ? 0 :
233 #if PLATFORM(WIN)
234         ENOENT;
235 #else
236         fnfErr;
237 #endif
238     return opened;
239 }
240
241 static CFIndex formRead(CFReadStreamRef, UInt8* buffer, CFIndex bufferLength, CFStreamError* error, Boolean* atEOF, void* context)
242 {
243     FormStreamFields* form = static_cast<FormStreamFields*>(context);
244
245     while (form->currentStream) {
246         CFIndex bytesToRead = bufferLength;
247         if (form->currentStreamRangeLength != BlobDataItem::toEndOfFile && form->currentStreamRangeLength < bytesToRead)
248             bytesToRead = static_cast<CFIndex>(form->currentStreamRangeLength);
249         CFIndex bytesRead = CFReadStreamRead(form->currentStream, buffer, bytesToRead);
250         if (bytesRead < 0) {
251             *error = CFReadStreamGetError(form->currentStream);
252             return -1;
253         }
254         if (bytesRead > 0) {
255             error->error = 0;
256             *atEOF = FALSE;
257             form->bytesSent += bytesRead;
258             if (form->currentStreamRangeLength != BlobDataItem::toEndOfFile)
259                 form->currentStreamRangeLength -= bytesRead;
260
261             return bytesRead;
262         }
263         openNextStream(form);
264     }
265
266     error->error = 0;
267     *atEOF = TRUE;
268     return 0;
269 }
270
271 static Boolean formCanRead(CFReadStreamRef stream, void* context)
272 {
273     FormStreamFields* form = static_cast<FormStreamFields*>(context);
274
275     while (form->currentStream && CFReadStreamGetStatus(form->currentStream) == kCFStreamStatusAtEnd)
276         openNextStream(form);
277
278     if (!form->currentStream) {
279         CFReadStreamSignalEvent(stream, kCFStreamEventEndEncountered, 0);
280         return FALSE;
281     }
282     return CFReadStreamHasBytesAvailable(form->currentStream);
283 }
284
285 static void formClose(CFReadStreamRef, void* context)
286 {
287     FormStreamFields* form = static_cast<FormStreamFields*>(context);
288
289     closeCurrentStream(form);
290 }
291
292 static CFTypeRef formCopyProperty(CFReadStreamRef, CFStringRef propertyName, void *context)
293 {
294     FormStreamFields* form = static_cast<FormStreamFields*>(context);
295
296     if (kCFCompareEqualTo == CFStringCompare(propertyName, formDataPointerPropertyName, 0)) {
297         long formDataAsNumber = static_cast<long>(reinterpret_cast<intptr_t>(form->formData.get()));
298         return CFNumberCreate(0, kCFNumberLongType, &formDataAsNumber);
299     }
300
301     if (kCFCompareEqualTo == CFStringCompare(propertyName, formDataStreamLengthPropertyName(), 0))
302         return CFStringCreateWithFormat(0, 0, CFSTR("%llu"), form->streamLength);
303
304     return 0;
305 }
306
307 static void formSchedule(CFReadStreamRef, CFRunLoopRef runLoop, CFStringRef runLoopMode, void* context)
308 {
309     FormStreamFields* form = static_cast<FormStreamFields*>(context);
310
311     if (form->currentStream)
312         CFReadStreamScheduleWithRunLoop(form->currentStream, runLoop, runLoopMode);
313     form->scheduledRunLoopPairs.add(SchedulePair::create(runLoop, runLoopMode));
314 }
315
316 static void formUnschedule(CFReadStreamRef, CFRunLoopRef runLoop, CFStringRef runLoopMode, void* context)
317 {
318     FormStreamFields* form = static_cast<FormStreamFields*>(context);
319
320     if (form->currentStream)
321         CFReadStreamUnscheduleFromRunLoop(form->currentStream, runLoop, runLoopMode);
322     form->scheduledRunLoopPairs.remove(SchedulePair::create(runLoop, runLoopMode));
323 }
324
325 static void formEventCallback(CFReadStreamRef stream, CFStreamEventType type, void* context)
326 {
327     FormStreamFields* form = static_cast<FormStreamFields*>(context);
328
329     switch (type) {
330     case kCFStreamEventHasBytesAvailable:
331         CFReadStreamSignalEvent(form->formStream, kCFStreamEventHasBytesAvailable, 0);
332         break;
333     case kCFStreamEventErrorOccurred: {
334         CFStreamError readStreamError = CFReadStreamGetError(stream);
335         CFReadStreamSignalEvent(form->formStream, kCFStreamEventErrorOccurred, &readStreamError);
336         break;
337     }
338     case kCFStreamEventEndEncountered:
339         openNextStream(form);
340         if (!form->currentStream)
341             CFReadStreamSignalEvent(form->formStream, kCFStreamEventEndEncountered, 0);
342         break;
343     case kCFStreamEventNone:
344         LOG_ERROR("unexpected kCFStreamEventNone");
345         break;
346     case kCFStreamEventOpenCompleted:
347         LOG_ERROR("unexpected kCFStreamEventOpenCompleted");
348         break;
349     case kCFStreamEventCanAcceptBytes:
350         LOG_ERROR("unexpected kCFStreamEventCanAcceptBytes");
351         break;
352     }
353 }
354
355 void setHTTPBody(CFMutableURLRequestRef request, PassRefPtr<FormData> prpFormData)
356 {
357     RefPtr<FormData> formData = prpFormData;
358
359     if (!formData)
360         return;
361         
362     size_t count = formData->elements().size();
363
364     // Handle the common special case of one piece of form data, with no files.
365     if (count == 1 && !formData->alwaysStream()) {
366         const FormDataElement& element = formData->elements()[0];
367         if (element.m_type == FormDataElement::Type::Data) {
368             RetainPtr<CFDataRef> data = adoptCF(CFDataCreate(0, reinterpret_cast<const UInt8 *>(element.m_data.data()), element.m_data.size()));
369             CFURLRequestSetHTTPRequestBody(request, data.get());
370             return;
371         }
372     }
373
374     formData = formData->resolveBlobReferences();
375     count = formData->elements().size();
376
377     // Precompute the content length so NSURLConnection doesn't use chunked mode.
378     unsigned long long length = 0;
379     for (size_t i = 0; i < count; ++i) {
380         const FormDataElement& element = formData->elements()[i];
381         if (element.m_type == FormDataElement::Type::Data)
382             length += element.m_data.size();
383         else {
384             // 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.
385             if (element.m_fileLength != BlobDataItem::toEndOfFile) {
386                 length += element.m_fileLength;
387                 continue;
388             }
389             long long fileSize;
390             if (getFileSize(element.m_shouldGenerateFile ? element.m_generatedFilename : element.m_filename, fileSize))
391                 length += fileSize;
392         }
393     }
394
395     // Create and set the stream.
396
397     // Pass the length along with the formData so it does not have to be recomputed.
398     FormCreationContext formContext = { formData.release(), length };
399
400     CFReadStreamCallBacksV1 callBacks = { 1, formCreate, formFinalize, 0, formOpen, 0, formRead, 0, formCanRead, formClose, formCopyProperty, 0, 0, formSchedule, formUnschedule
401     };
402     RetainPtr<CFReadStreamRef> stream = adoptCF(CFReadStreamCreate(0, static_cast<const void*>(&callBacks), &formContext));
403
404     CFURLRequestSetHTTPRequestBodyStream(request, stream.get());
405 }
406
407 FormData* httpBodyFromStream(CFReadStreamRef stream)
408 {
409     if (!stream)
410         return 0;
411
412     // Passing the pointer as property appears to be the only way to associate a stream with FormData.
413     // A new stream is always created in CFURLRequestCopyHTTPRequestBodyStream (or -[NSURLRequest HTTPBodyStream]),
414     // so a side HashMap wouldn't work.
415     // Even the stream's context pointer is different from the one we returned from formCreate().
416
417     RetainPtr<CFNumberRef> formDataPointerAsCFNumber = adoptCF(static_cast<CFNumberRef>(CFReadStreamCopyProperty(stream, formDataPointerPropertyName)));
418     if (!formDataPointerAsCFNumber)
419         return 0;
420
421     long formDataPointerAsNumber;
422     if (!CFNumberGetValue(formDataPointerAsCFNumber.get(), kCFNumberLongType, &formDataPointerAsNumber))
423         return 0;
424
425     return reinterpret_cast<FormData*>(static_cast<intptr_t>(formDataPointerAsNumber));
426 }
427
428 } // namespace WebCore