Replace WTF::move with WTFMove
[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 = WTFMove(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 formFinalize(CFReadStreamRef stream, void* context)
211 {
212     FormStreamFields* form = static_cast<FormStreamFields*>(context);
213     ASSERT_UNUSED(stream, form->formStream == stream);
214
215     callOnMainThread([form] {
216         closeCurrentStream(form);
217         delete form;
218     });
219 }
220
221 static Boolean formOpen(CFReadStreamRef, CFStreamError* error, Boolean* openComplete, void* context)
222 {
223     FormStreamFields* form = static_cast<FormStreamFields*>(context);
224
225     bool opened = openNextStream(form);
226
227     *openComplete = opened;
228     error->error = opened ? 0 :
229 #if PLATFORM(WIN)
230         ENOENT;
231 #else
232         fnfErr;
233 #endif
234     return opened;
235 }
236
237 static CFIndex formRead(CFReadStreamRef, UInt8* buffer, CFIndex bufferLength, CFStreamError* error, Boolean* atEOF, void* context)
238 {
239     FormStreamFields* form = static_cast<FormStreamFields*>(context);
240
241     while (form->currentStream) {
242         CFIndex bytesToRead = bufferLength;
243         if (form->currentStreamRangeLength != BlobDataItem::toEndOfFile && form->currentStreamRangeLength < bytesToRead)
244             bytesToRead = static_cast<CFIndex>(form->currentStreamRangeLength);
245         CFIndex bytesRead = CFReadStreamRead(form->currentStream, buffer, bytesToRead);
246         if (bytesRead < 0) {
247             *error = CFReadStreamGetError(form->currentStream);
248             return -1;
249         }
250         if (bytesRead > 0) {
251             error->error = 0;
252             *atEOF = FALSE;
253             form->bytesSent += bytesRead;
254             if (form->currentStreamRangeLength != BlobDataItem::toEndOfFile)
255                 form->currentStreamRangeLength -= bytesRead;
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         CFReadStreamSignalEvent(stream, kCFStreamEventEndEncountered, 0);
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 CFTypeRef formCopyProperty(CFReadStreamRef, CFStringRef propertyName, void *context)
289 {
290     FormStreamFields* form = static_cast<FormStreamFields*>(context);
291
292     if (kCFCompareEqualTo == CFStringCompare(propertyName, formDataPointerPropertyName, 0)) {
293         long formDataAsNumber = static_cast<long>(reinterpret_cast<intptr_t>(form->formData.get()));
294         return CFNumberCreate(0, kCFNumberLongType, &formDataAsNumber);
295     }
296
297     if (kCFCompareEqualTo == CFStringCompare(propertyName, formDataStreamLengthPropertyName(), 0))
298         return CFStringCreateWithFormat(0, 0, CFSTR("%llu"), form->streamLength);
299
300     return 0;
301 }
302
303 static void formSchedule(CFReadStreamRef, CFRunLoopRef runLoop, CFStringRef runLoopMode, void* context)
304 {
305     FormStreamFields* form = static_cast<FormStreamFields*>(context);
306
307     if (form->currentStream)
308         CFReadStreamScheduleWithRunLoop(form->currentStream, runLoop, runLoopMode);
309     form->scheduledRunLoopPairs.add(SchedulePair::create(runLoop, runLoopMode));
310 }
311
312 static void formUnschedule(CFReadStreamRef, CFRunLoopRef runLoop, CFStringRef runLoopMode, void* context)
313 {
314     FormStreamFields* form = static_cast<FormStreamFields*>(context);
315
316     if (form->currentStream)
317         CFReadStreamUnscheduleFromRunLoop(form->currentStream, runLoop, runLoopMode);
318     form->scheduledRunLoopPairs.remove(SchedulePair::create(runLoop, runLoopMode));
319 }
320
321 static void formEventCallback(CFReadStreamRef stream, CFStreamEventType type, void* context)
322 {
323     FormStreamFields* form = static_cast<FormStreamFields*>(context);
324
325     switch (type) {
326     case kCFStreamEventHasBytesAvailable:
327         CFReadStreamSignalEvent(form->formStream, kCFStreamEventHasBytesAvailable, 0);
328         break;
329     case kCFStreamEventErrorOccurred: {
330         CFStreamError readStreamError = CFReadStreamGetError(stream);
331         CFReadStreamSignalEvent(form->formStream, kCFStreamEventErrorOccurred, &readStreamError);
332         break;
333     }
334     case kCFStreamEventEndEncountered:
335         openNextStream(form);
336         if (!form->currentStream)
337             CFReadStreamSignalEvent(form->formStream, kCFStreamEventEndEncountered, 0);
338         break;
339     case kCFStreamEventNone:
340         LOG_ERROR("unexpected kCFStreamEventNone");
341         break;
342     case kCFStreamEventOpenCompleted:
343         LOG_ERROR("unexpected kCFStreamEventOpenCompleted");
344         break;
345     case kCFStreamEventCanAcceptBytes:
346         LOG_ERROR("unexpected kCFStreamEventCanAcceptBytes");
347         break;
348     }
349 }
350
351 void setHTTPBody(CFMutableURLRequestRef request, PassRefPtr<FormData> prpFormData)
352 {
353     RefPtr<FormData> formData = prpFormData;
354
355     if (!formData)
356         return;
357         
358     size_t count = formData->elements().size();
359
360     // Handle the common special case of one piece of form data, with no files.
361     if (count == 1 && !formData->alwaysStream()) {
362         const FormDataElement& element = formData->elements()[0];
363         if (element.m_type == FormDataElement::Type::Data) {
364             RetainPtr<CFDataRef> data = adoptCF(CFDataCreate(0, reinterpret_cast<const UInt8 *>(element.m_data.data()), element.m_data.size()));
365             CFURLRequestSetHTTPRequestBody(request, data.get());
366             return;
367         }
368     }
369
370     formData = formData->resolveBlobReferences();
371     count = formData->elements().size();
372
373     // Precompute the content length so NSURLConnection doesn't use chunked mode.
374     unsigned long long length = 0;
375     for (size_t i = 0; i < count; ++i) {
376         const FormDataElement& element = formData->elements()[i];
377         if (element.m_type == FormDataElement::Type::Data)
378             length += element.m_data.size();
379         else {
380             // 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.
381             if (element.m_fileLength != BlobDataItem::toEndOfFile) {
382                 length += element.m_fileLength;
383                 continue;
384             }
385             long long fileSize;
386             if (getFileSize(element.m_shouldGenerateFile ? element.m_generatedFilename : element.m_filename, fileSize))
387                 length += fileSize;
388         }
389     }
390
391     // Create and set the stream.
392
393     // Pass the length along with the formData so it does not have to be recomputed.
394     FormCreationContext formContext = { formData.release(), length };
395
396     CFReadStreamCallBacksV1 callBacks = { 1, formCreate, formFinalize, 0, formOpen, 0, formRead, 0, formCanRead, formClose, formCopyProperty, 0, 0, formSchedule, formUnschedule
397     };
398     RetainPtr<CFReadStreamRef> stream = adoptCF(CFReadStreamCreate(0, static_cast<const void*>(&callBacks), &formContext));
399
400     CFURLRequestSetHTTPRequestBodyStream(request, stream.get());
401 }
402
403 FormData* httpBodyFromStream(CFReadStreamRef stream)
404 {
405     if (!stream)
406         return 0;
407
408     // Passing the pointer as property appears to be the only way to associate a stream with FormData.
409     // A new stream is always created in CFURLRequestCopyHTTPRequestBodyStream (or -[NSURLRequest HTTPBodyStream]),
410     // so a side HashMap wouldn't work.
411     // Even the stream's context pointer is different from the one we returned from formCreate().
412
413     RetainPtr<CFNumberRef> formDataPointerAsCFNumber = adoptCF(static_cast<CFNumberRef>(CFReadStreamCopyProperty(stream, formDataPointerPropertyName)));
414     if (!formDataPointerAsCFNumber)
415         return 0;
416
417     long formDataPointerAsNumber;
418     if (!CFNumberGetValue(formDataPointerAsCFNumber.get(), kCFNumberLongType, &formDataPointerAsNumber))
419         return 0;
420
421     return reinterpret_cast<FormData*>(static_cast<intptr_t>(formDataPointerAsNumber));
422 }
423
424 } // namespace WebCore