73b245159ba3c8089719257096dd35a626a738fc
[WebKit-https.git] / Source / WebCore / platform / network / curl / MultipartHandle.cpp
1 /*
2  * Copyright (C) 2013 University of Szeged
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
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  *
14  * THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF SZEGED ``AS IS'' AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL UNIVERSITY OF SZEGED OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26 #include "config.h"
27 #include "MultipartHandle.h"
28
29 #if USE(CURL)
30
31 #include "HTTPHeaderNames.h"
32 #include "HTTPParsers.h"
33 #include "ResourceHandleClient.h"
34 #include "ResourceHandleInternal.h"
35 #include "ResourceResponse.h"
36 #include "SharedBuffer.h"
37 #include <wtf/StringExtras.h>
38
39 namespace WebCore {
40
41 bool MultipartHandle::extractBoundary(const String& contentType, String& boundary)
42 {
43     static const size_t length = strlen("boundary=");
44     size_t boundaryStart = contentType.findIgnoringASCIICase("boundary=");
45
46     // No boundary found.
47     if (boundaryStart == notFound)
48         return false;
49
50     boundaryStart += length;
51     size_t boundaryEnd = 0;
52
53     if (contentType[boundaryStart] == '"') {
54         // Boundary value starts with a " quote. Search for the closing one.
55         ++boundaryStart;
56         boundaryEnd = contentType.find('"', boundaryStart);
57         if (boundaryEnd == notFound)
58             return false;
59     } else if (contentType[boundaryStart] == '\'') {
60         // Boundary value starts with a ' quote. Search for the closing one.
61         ++boundaryStart;
62         boundaryEnd = contentType.find('\'', boundaryStart);
63         if (boundaryEnd == notFound)
64             return false;
65     } else {
66         // Check for the end of the boundary. That can be a semicolon or a newline.
67         boundaryEnd = contentType.find(';', boundaryStart);
68         if (boundaryEnd == notFound)
69             boundaryEnd = contentType.length();
70     }
71
72     // The boundary end should not be before the start
73     if (boundaryEnd <= boundaryStart)
74         return false;
75
76     boundary = contentType.substring(boundaryStart, boundaryEnd - boundaryStart);
77     return true;
78 }
79
80
81 bool MultipartHandle::matchForBoundary(const char* data, size_t position, size_t& matchedLength)
82 {
83     matchedLength = 0;
84
85     for (size_t i = 0; i < m_boundaryLength; ++i) {
86         if (data[position + i] != m_boundary[i]) {
87             matchedLength = i;
88             return false;
89         }
90     }
91
92     matchedLength = m_boundaryLength;
93     return true;
94 }
95
96 bool MultipartHandle::checkForBoundary(size_t& boundaryStartPosition, size_t& lastPartialMatchPosition)
97 {
98     size_t contentLength = m_buffer.size();
99
100     boundaryStartPosition = notFound;
101     lastPartialMatchPosition = contentLength;
102
103     if (contentLength < m_boundaryLength)
104         return false;
105
106     const char* content = m_buffer.data();
107     size_t matched;
108
109     for (size_t i = 0; i < contentLength - m_boundaryLength; ++i) {
110         if (matchForBoundary(content, i, matched)) {
111             boundaryStartPosition = i;
112             return true;
113         }
114
115         if (matched)
116             lastPartialMatchPosition = i;
117     }
118
119     return false;
120 }
121
122 bool MultipartHandle::parseHeadersIfPossible()
123 {
124     size_t contentLength = m_buffer.size();
125
126     if (!contentLength)
127         return false;
128
129     const char* content = m_buffer.data();
130
131     // Check if we have the header closing strings.
132     if (!strnstr(content, "\r\n\r\n", contentLength)) {
133         // Some servers closes the headers with only \n-s.
134         if (!strnstr(content, "\n\n", contentLength)) {
135             // Don't have the header closing string. Wait for more data.
136             return false;
137         }
138     }
139
140     // Parse the HTTP headers.
141     String value;
142     StringView name;
143     char* p = const_cast<char*>(content);
144     const char* end = content + contentLength;
145     size_t totalConsumedLength = 0;
146     for (; p < end; ++p) {
147         String failureReason;
148         size_t consumedLength = parseHTTPHeader(p, end - p, failureReason, name, value, false);
149         if (!consumedLength)
150             break; // No more header to parse.
151
152         p += consumedLength;
153         totalConsumedLength += consumedLength;
154
155         // The name should not be empty, but the value could be empty.
156         if (name.isEmpty())
157             break;
158
159         m_headers.add(name.toString(), value);
160     }
161
162     m_buffer.remove(0, totalConsumedLength + 1);
163     return true;
164 }
165
166 void MultipartHandle::contentReceived(const char* data, size_t length)
167 {
168     if (m_state == End)
169         return; // The handler is closed down so ignore everything.
170
171     m_buffer.append(data, length);
172
173     while (processContent()) { }
174 }
175
176 bool MultipartHandle::processContent()
177 {
178 /*
179     The allowed transitions between the states:
180          Check Boundary
181                |
182       /-- In Boundary <----\
183      |         |            |
184      |     In Header        |
185      |         |            |
186      |     In Content       |
187      |         |            |
188      |    End Boundary ----/
189      |         |
190       \-----> End
191
192 */
193     switch (m_state) {
194     case CheckBoundary: {
195         if (m_buffer.size() < m_boundaryLength) {
196             // We don't have enough data, so just skip.
197             return false;
198         }
199
200         // Check for the boundary string.
201         size_t boundaryStart;
202         size_t lastPartialMatch;
203
204         if (!checkForBoundary(boundaryStart, lastPartialMatch) && boundaryStart == notFound) {
205             // Did not find the boundary start in this chunk.
206             // Skip ahead to the last valid looking boundary character and start again.
207             m_buffer.remove(0, lastPartialMatch);
208             return false;
209         }
210
211         // Found the boundary start.
212         // Consume everything before that and also the boundary
213         m_buffer.remove(0, boundaryStart + m_boundaryLength);
214         m_state = InBoundary;
215     }
216     // Fallthrough.
217     case InBoundary: {
218         // Now the first two characters should be: \r\n
219         if (m_buffer.size() < 2)
220             return false;
221
222         const char* content = m_buffer.data();
223         // By default we'll remove 2 characters at the end.
224         // The \r and \n as stated in the multipart RFC.
225         size_t removeCount = 2;
226
227         if (content[0] != '\r' || content[1] != '\n') {
228             // There should be a \r and a \n but it seems that's not the case.
229             // So we'll check for a simple \n. Not really RFC compatible but servers do tricky things.
230             if (content[0] != '\n') {
231                 // Also no \n so just go to the end.
232                 m_state = End;
233                 return false;
234             }
235
236             // Found a simple \n so remove just that.
237             removeCount = 1;
238         }
239
240         // Consume the characters.
241         m_buffer.remove(0, removeCount);
242         m_headers.clear();
243         m_state = InHeader;
244     }
245     // Fallthrough.
246     case InHeader: {
247         // Process the headers.
248         if (!parseHeadersIfPossible()) {
249             // Parsing of headers failed, try again later.
250             return false;
251         }
252
253         didReceiveResponse();
254         m_state = InContent;
255     }
256     // Fallthrough.
257     case InContent: {
258         if (m_buffer.isEmpty())
259             return false;
260
261         size_t boundaryStart = notFound;
262         size_t lastPartialMatch;
263
264         if (!checkForBoundary(boundaryStart, lastPartialMatch) && boundaryStart == notFound) {
265             // Did not find the boundary start, all data up to the lastPartialMatch is ok.
266             didReceiveData(lastPartialMatch);
267             m_buffer.remove(0, lastPartialMatch);
268             return false;
269         }
270
271         // There was a boundary start (or end we'll check that later), push out part of the data.
272         didReceiveData(boundaryStart);
273         m_buffer.remove(0, boundaryStart + m_boundaryLength);
274         m_state = EndBoundary;
275     }
276     // Fallthrough.
277     case EndBoundary: {
278         if (m_buffer.size() < 2)
279             return false; // Not enough data to check. Return later when there is more data.
280
281         // We'll decide if this is a closing boundary or an opening one.
282         const char* content = m_buffer.data();
283
284         if (content[0] == '-' && content[1] == '-') {
285             // This is a closing boundary. Close down the handler.
286             m_state = End;
287             return false;
288         }
289
290         // This was a simple content separator not a closing one.
291         // Go to before the content processing.
292         m_state = InBoundary;
293         break;
294     }
295     case End:
296         // We are done. Nothing to do anymore.
297         return false;
298     default:
299         ASSERT_NOT_REACHED();
300         return false;
301     }
302
303     return true; // There are still things to process, so go for it.
304 }
305
306 void MultipartHandle::contentEnded()
307 {
308     // Process the leftover data.
309     while (processContent()) { }
310
311     if (m_state != End) {
312         // It seems we are still not at the end of the processing.
313         // Push out the remaining data.
314         didReceiveData(m_buffer.size());
315         m_state = End;
316     }
317
318     m_buffer.clear();
319 }
320
321 void MultipartHandle::didReceiveData(size_t length)
322 {
323     ResourceHandleInternal* d = m_resourceHandle->getInternal();
324
325     if (!d->m_delegate->hasHandle()) {
326         // Request has been canceled, so we'll go to the end state.
327         m_state = End;
328         return;
329     }
330
331     if (d->client()) {
332         const char* data = m_buffer.data();
333         d->client()->didReceiveBuffer(m_resourceHandle, SharedBuffer::create(data, length), length);
334     }
335 }
336
337 void MultipartHandle::didReceiveResponse()
338 {
339     ResourceHandleInternal* d = m_resourceHandle->getInternal();
340     if (d->client()) {
341         auto response = d->m_response;
342
343         HTTPHeaderMap::const_iterator end = m_headers.end();
344         for (HTTPHeaderMap::const_iterator it = m_headers.begin(); it != end; ++it)
345             response.setHTTPHeaderField(it->key, it->value);
346
347         String contentType = m_headers.get(HTTPHeaderName::ContentType);
348         String mimeType = extractMIMETypeFromMediaType(contentType);
349
350         response.setMimeType(mimeType.convertToASCIILowercase());
351         response.setTextEncodingName(extractCharsetFromMediaType(contentType));
352
353         m_resourceHandle->didReceiveResponse(WTFMove(response));
354     }
355 }
356
357 } // namespace WebCore
358
359 #endif