84f5e2b942b8a21cc2c4500fc35e11df095a66e8
[WebKit-https.git] / Source / WebCore / platform / network / curl / CurlFormDataStream.cpp
1 /*
2  * Copyright (C) 2008 Apple Inc.  All rights reserved.
3  * Copyright (C) 2006 Michael Emmel mike.emmel@gmail.com
4  * Copyright (C) 2007 Alp Toker <alp.toker@collabora.co.uk>
5  * Copyright (C) 2007 Holger Hans Peter Freyther
6  * Copyright (C) 2008 Collabora Ltd.
7  * Copyright (C) 2018 Sony Interactive Entertainment Inc.
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
20  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
23  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
27  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31
32 #include "config.h"
33 #include "CurlFormDataStream.h"
34
35 #if USE(CURL)
36
37 #include "CurlContext.h"
38 #include "Logging.h"
39 #include <wtf/MainThread.h>
40
41 namespace WebCore {
42
43 CurlFormDataStream::CurlFormDataStream(const FormData* formData)
44 {
45     ASSERT(isMainThread());
46
47     if (!formData || formData->isEmpty())
48         return;
49
50     m_formData = formData->isolatedCopy();
51
52     // Resolve the blob elements so the formData can correctly report it's size.
53     m_formData = m_formData->resolveBlobReferences();
54 }
55
56 CurlFormDataStream::~CurlFormDataStream()
57 {
58
59 }
60
61 void CurlFormDataStream::clean()
62 {
63     if (m_postData)
64         m_postData = nullptr;
65
66     if (m_fileHandle != FileSystem::invalidPlatformFileHandle) {
67         FileSystem::closeFile(m_fileHandle);
68         m_fileHandle = FileSystem::invalidPlatformFileHandle;
69     }
70 }
71
72 std::optional<const Vector<char>&> CurlFormDataStream::getPostData()
73 {
74     if (!m_formData)
75         return std::nullopt;
76
77     if (!m_postData)
78         m_postData = std::make_unique<Vector<char>>(m_formData->flatten());
79
80     return *m_postData;
81 }
82
83 bool CurlFormDataStream::shouldUseChunkTransfer()
84 {
85     computeContentLength();
86
87     return m_shouldUseChunkTransfer;
88 }
89
90 unsigned long long CurlFormDataStream::totalSize()
91 {
92     computeContentLength();
93
94     return m_totalSize;
95 }
96
97 void CurlFormDataStream::computeContentLength()
98 {
99     static auto maxCurlOffT = CurlHandle::maxCurlOffT();
100
101     if (!m_formData || m_isContentLengthUpdated)
102         return;
103
104     m_isContentLengthUpdated = true;
105
106     for (const auto& element : m_formData->elements()) {
107         if (element.m_type == FormDataElement::Type::EncodedFile) {
108             long long fileSize;
109             if (FileSystem::getFileSize(element.m_filename, fileSize)) {
110                 if (fileSize > maxCurlOffT) {
111                     // File size is too big for specifying it to curl
112                     m_shouldUseChunkTransfer = true;
113                 }
114
115                 m_totalSize += fileSize;
116             } else
117                 m_shouldUseChunkTransfer = true;
118         } else
119             m_totalSize += element.m_data.size();
120     }
121 }
122
123 std::optional<size_t> CurlFormDataStream::read(char* buffer, size_t size)
124 {
125     if (!m_formData)
126         return std::nullopt;
127
128     const auto totalElementSize = m_formData->elements().size();
129     if (m_elementPosition >= totalElementSize)
130         return 0;
131
132     size_t totalReadBytes = 0;
133
134     while ((m_elementPosition < totalElementSize) && (totalReadBytes < size)) {
135         const auto& element = m_formData->elements().at(m_elementPosition);
136
137         std::optional<size_t> readBytes;
138         size_t bufferSize = size - totalReadBytes;
139         char* bufferPosition = buffer + totalReadBytes;
140
141         if (element.m_type == FormDataElement::Type::EncodedFile)
142             readBytes = readFromFile(element, bufferPosition, bufferSize);
143         else
144             readBytes = readFromData(element, bufferPosition, bufferSize);
145
146         if (!readBytes)
147             return std::nullopt;
148
149         totalReadBytes += *readBytes;
150     }
151
152     m_totalReadSize += totalReadBytes;
153
154     return totalReadBytes;
155 }
156
157 std::optional<size_t> CurlFormDataStream::readFromFile(const FormDataElement& element, char* buffer, size_t size)
158 {
159     if (m_fileHandle == FileSystem::invalidPlatformFileHandle)
160         m_fileHandle = FileSystem::openFile(element.m_filename, FileSystem::FileOpenMode::Read);
161
162     if (!FileSystem::isHandleValid(m_fileHandle)) {
163         LOG(Network, "Curl - Failed while trying to open %s for upload\n", element.m_filename.utf8().data());
164         m_fileHandle = FileSystem::invalidPlatformFileHandle;
165         return std::nullopt;
166     }
167
168     auto readBytes = FileSystem::readFromFile(m_fileHandle, buffer, size);
169     if (readBytes < 0) {
170         LOG(Network, "Curl - Failed while trying to read %s for upload\n", element.m_filename.utf8().data());
171         FileSystem::closeFile(m_fileHandle);
172         m_fileHandle = FileSystem::invalidPlatformFileHandle;
173         return std::nullopt;
174     }
175
176     if (!readBytes) {
177         FileSystem::closeFile(m_fileHandle);
178         m_fileHandle = FileSystem::invalidPlatformFileHandle;
179         m_elementPosition++;
180     }
181
182     return readBytes;
183 }
184
185 std::optional<size_t> CurlFormDataStream::readFromData(const FormDataElement& element, char* buffer, size_t size)
186 {
187     size_t elementSize = element.m_data.size() - m_dataOffset;
188     const char* elementBuffer = element.m_data.data() + m_dataOffset;
189
190     size_t readBytes = elementSize > size ? size : elementSize;
191     memcpy(buffer, elementBuffer, readBytes);
192
193     if (elementSize > readBytes)
194         m_dataOffset += readBytes;
195     else {
196         m_dataOffset = 0;
197         m_elementPosition++;
198     }
199
200     return readBytes;
201 }
202
203 } // namespace WebCore
204
205 #endif