[Curl] Extract multipart handling from ResourceHandle to CurlRequest.
authordon.olmstead@sony.com <don.olmstead@sony.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 11 Jan 2018 21:36:28 +0000 (21:36 +0000)
committerdon.olmstead@sony.com <don.olmstead@sony.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 11 Jan 2018 21:36:28 +0000 (21:36 +0000)
https://bugs.webkit.org/show_bug.cgi?id=181506

Patch by Basuke Suzuki <Basuke.Suzuki@sony.com> on 2018-01-11
Reviewed by Alex Christensen.

Rename old MultipartHandle class to CurlMultipartHandle and modernize it. Also move the responsibility
of handling multi part from ResourceHandle to CurlRequest. This is required for upcoming NetworkLoadTask.

No new tests because no new behavior.

* platform/Curl.cmake:
* platform/network/curl/CurlMultipartHandle.cpp: Renamed from Source/WebCore/platform/network/curl/MultipartHandle.cpp.
(WebCore::CurlMultipartHandle::createIfNeeded):
(WebCore::CurlMultipartHandle::extractBoundary):
(WebCore::CurlMultipartHandle::extractBoundaryFromContentType):
(WebCore::CurlMultipartHandle::CurlMultipartHandle):
(WebCore::CurlMultipartHandle::didReceiveData):
(WebCore::CurlMultipartHandle::didComplete):
(WebCore::CurlMultipartHandle::processContent):
(WebCore::CurlMultipartHandle::checkForBoundary):
(WebCore::CurlMultipartHandle::matchedLength):
(WebCore::CurlMultipartHandle::parseHeadersIfPossible):
* platform/network/curl/CurlMultipartHandle.h: Renamed from Source/WebCore/platform/network/curl/MultipartHandle.h.
(WebCore::CurlMultipartHandle::~CurlMultipartHandle):
* platform/network/curl/CurlMultipartHandleClient.h: Added.
(WebCore::CurlMultipartHandleClient::~CurlMultipartHandleClient):
* platform/network/curl/CurlRequest.cpp:
(WebCore::CurlRequest::CurlRequest):
(WebCore::CurlRequest::didReceiveHeader):
(WebCore::CurlRequest::didReceiveData):
(WebCore::CurlRequest::didReceiveHeaderFromMultipart):
(WebCore::CurlRequest::didReceiveDataFromMultipart):
(WebCore::CurlRequest::didCompleteTransfer):
(WebCore::CurlRequest::finalizeTransfer):
(WebCore::CurlRequest::invokeDidReceiveResponseForFile):
(WebCore::CurlRequest::invokeDidReceiveResponse):
(WebCore::CurlRequest::completeDidReceiveResponse):
* platform/network/curl/CurlRequest.h:
(WebCore::CurlRequest::create):
* platform/network/curl/ResourceHandleCurlDelegate.cpp:
(WebCore::ResourceHandleCurlDelegate::createCurlRequest):
(WebCore::ResourceHandleCurlDelegate::curlDidReceiveResponse):
(WebCore::ResourceHandleCurlDelegate::curlDidReceiveBuffer):
(WebCore::ResourceHandleCurlDelegate::curlDidComplete):
* platform/network/curl/ResourceHandleCurlDelegate.h:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@226800 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/WebCore/ChangeLog
Source/WebCore/platform/Curl.cmake
Source/WebCore/platform/network/curl/CurlMultipartHandle.cpp [moved from Source/WebCore/platform/network/curl/MultipartHandle.cpp with 65% similarity]
Source/WebCore/platform/network/curl/CurlMultipartHandle.h [moved from Source/WebCore/platform/network/curl/MultipartHandle.h with 65% similarity]
Source/WebCore/platform/network/curl/CurlMultipartHandleClient.h [new file with mode: 0644]
Source/WebCore/platform/network/curl/CurlRequest.cpp
Source/WebCore/platform/network/curl/CurlRequest.h
Source/WebCore/platform/network/curl/ResourceHandleCurlDelegate.cpp
Source/WebCore/platform/network/curl/ResourceHandleCurlDelegate.h

index 868b815..9a9f17c 100644 (file)
@@ -1,3 +1,51 @@
+2018-01-11  Basuke Suzuki  <Basuke.Suzuki@sony.com>
+
+        [Curl] Extract multipart handling from ResourceHandle to CurlRequest.
+        https://bugs.webkit.org/show_bug.cgi?id=181506
+
+        Reviewed by Alex Christensen.
+
+        Rename old MultipartHandle class to CurlMultipartHandle and modernize it. Also move the responsibility 
+        of handling multi part from ResourceHandle to CurlRequest. This is required for upcoming NetworkLoadTask.
+
+        No new tests because no new behavior.
+
+        * platform/Curl.cmake:
+        * platform/network/curl/CurlMultipartHandle.cpp: Renamed from Source/WebCore/platform/network/curl/MultipartHandle.cpp.
+        (WebCore::CurlMultipartHandle::createIfNeeded):
+        (WebCore::CurlMultipartHandle::extractBoundary):
+        (WebCore::CurlMultipartHandle::extractBoundaryFromContentType):
+        (WebCore::CurlMultipartHandle::CurlMultipartHandle):
+        (WebCore::CurlMultipartHandle::didReceiveData):
+        (WebCore::CurlMultipartHandle::didComplete):
+        (WebCore::CurlMultipartHandle::processContent):
+        (WebCore::CurlMultipartHandle::checkForBoundary):
+        (WebCore::CurlMultipartHandle::matchedLength):
+        (WebCore::CurlMultipartHandle::parseHeadersIfPossible):
+        * platform/network/curl/CurlMultipartHandle.h: Renamed from Source/WebCore/platform/network/curl/MultipartHandle.h.
+        (WebCore::CurlMultipartHandle::~CurlMultipartHandle):
+        * platform/network/curl/CurlMultipartHandleClient.h: Added.
+        (WebCore::CurlMultipartHandleClient::~CurlMultipartHandleClient):
+        * platform/network/curl/CurlRequest.cpp:
+        (WebCore::CurlRequest::CurlRequest):
+        (WebCore::CurlRequest::didReceiveHeader):
+        (WebCore::CurlRequest::didReceiveData):
+        (WebCore::CurlRequest::didReceiveHeaderFromMultipart):
+        (WebCore::CurlRequest::didReceiveDataFromMultipart):
+        (WebCore::CurlRequest::didCompleteTransfer):
+        (WebCore::CurlRequest::finalizeTransfer):
+        (WebCore::CurlRequest::invokeDidReceiveResponseForFile):
+        (WebCore::CurlRequest::invokeDidReceiveResponse):
+        (WebCore::CurlRequest::completeDidReceiveResponse):
+        * platform/network/curl/CurlRequest.h:
+        (WebCore::CurlRequest::create):
+        * platform/network/curl/ResourceHandleCurlDelegate.cpp:
+        (WebCore::ResourceHandleCurlDelegate::createCurlRequest):
+        (WebCore::ResourceHandleCurlDelegate::curlDidReceiveResponse):
+        (WebCore::ResourceHandleCurlDelegate::curlDidReceiveBuffer):
+        (WebCore::ResourceHandleCurlDelegate::curlDidComplete):
+        * platform/network/curl/ResourceHandleCurlDelegate.h:
+
 2018-01-11  Zalan Bujtas  <zalan@apple.com>
 
         RenderTreeUpdater::current() returns null_ptr when mutation is done through Document::resolveStyle.
index 874a39e..cdaef5b 100644 (file)
@@ -12,12 +12,12 @@ list(APPEND WebCore_SOURCES
     platform/network/curl/CurlContext.cpp
     platform/network/curl/CurlDownload.cpp
     platform/network/curl/CurlFormDataStream.cpp
+    platform/network/curl/CurlMultipartHandle.cpp
     platform/network/curl/CurlRequest.cpp
     platform/network/curl/CurlRequestScheduler.cpp
     platform/network/curl/CurlSSLHandle.cpp
     platform/network/curl/CurlSSLVerifier.cpp
     platform/network/curl/DNSCurl.cpp
-    platform/network/curl/MultipartHandle.cpp
     platform/network/curl/NetworkStorageSessionCurl.cpp
     platform/network/curl/ProxyServerCurl.cpp
     platform/network/curl/ResourceErrorCurl.cpp
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2013 University of Szeged
+ * Copyright (C) 2018 Sony Interactive Entertainment Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
+
 #include "config.h"
-#include "MultipartHandle.h"
+#include "CurlMultipartHandle.h"
 
 #if USE(CURL)
 
+#include "CurlMultipartHandleClient.h"
+#include "CurlResponse.h"
 #include "HTTPHeaderNames.h"
 #include "HTTPParsers.h"
-#include "ResourceHandleClient.h"
-#include "ResourceHandleInternal.h"
 #include "ResourceResponse.h"
 #include "SharedBuffer.h"
 #include <wtf/StringExtras.h>
 
 namespace WebCore {
 
-bool MultipartHandle::extractBoundary(const String& contentType, String& boundary)
+std::unique_ptr<CurlMultipartHandle> CurlMultipartHandle::createIfNeeded(CurlMultipartHandleClient& client, const CurlResponse& response)
+{
+    auto boundary = extractBoundary(response);
+    if (!boundary)
+        return nullptr;
+
+    return std::make_unique<CurlMultipartHandle>(client, *boundary);
+}
+
+std::optional<String> CurlMultipartHandle::extractBoundary(const CurlResponse& response)
+{
+    for (auto header : response.headers) {
+        auto splitPosistion = header.find(":");
+        if (splitPosistion == notFound)
+            continue;
+
+        auto key = header.left(splitPosistion).stripWhiteSpace();
+        if (!equalIgnoringASCIICase(key, "Content-Type"))
+            continue;
+
+        auto contentType = header.substring(splitPosistion + 1).stripWhiteSpace();
+        auto mimeType = extractMIMETypeFromMediaType(contentType);
+        if (!equalIgnoringASCIICase(mimeType, "multipart/x-mixed-replace"))
+            continue;
+
+        auto boundary = extractBoundaryFromContentType(contentType);
+        if (!boundary)
+            continue;
+
+        return String("--" + *boundary);
+    }
+
+    return std::nullopt;
+}
+
+std::optional<String> CurlMultipartHandle::extractBoundaryFromContentType(const String& contentType)
 {
     static const size_t length = strlen("boundary=");
-    size_t boundaryStart = contentType.findIgnoringASCIICase("boundary=");
 
-    // No boundary found.
+    auto boundaryStart = contentType.findIgnoringASCIICase("boundary=");
     if (boundaryStart == notFound)
-        return false;
+        return std::nullopt;
 
     boundaryStart += length;
     size_t boundaryEnd = 0;
@@ -55,13 +91,13 @@ bool MultipartHandle::extractBoundary(const String& contentType, String& boundar
         ++boundaryStart;
         boundaryEnd = contentType.find('"', boundaryStart);
         if (boundaryEnd == notFound)
-            return false;
+            return std::nullopt;
     } else if (contentType[boundaryStart] == '\'') {
         // Boundary value starts with a ' quote. Search for the closing one.
         ++boundaryStart;
         boundaryEnd = contentType.find('\'', boundaryStart);
         if (boundaryEnd == notFound)
-            return false;
+            return std::nullopt;
     } else {
         // Check for the end of the boundary. That can be a semicolon or a newline.
         boundaryEnd = contentType.find(';', boundaryStart);
@@ -71,109 +107,44 @@ bool MultipartHandle::extractBoundary(const String& contentType, String& boundar
 
     // The boundary end should not be before the start
     if (boundaryEnd <= boundaryStart)
-        return false;
+        return std::nullopt;
 
-    boundary = contentType.substring(boundaryStart, boundaryEnd - boundaryStart);
-    return true;
+    return contentType.substring(boundaryStart, boundaryEnd - boundaryStart);
 }
 
-
-bool MultipartHandle::matchForBoundary(const char* data, size_t position, size_t& matchedLength)
+CurlMultipartHandle::CurlMultipartHandle(CurlMultipartHandleClient& client, const String& boundary)
+    : m_client(client)
+    , m_boundary(boundary)
 {
-    matchedLength = 0;
 
-    for (size_t i = 0; i < m_boundaryLength; ++i) {
-        if (data[position + i] != m_boundary[i]) {
-            matchedLength = i;
-            return false;
-        }
-    }
-
-    matchedLength = m_boundaryLength;
-    return true;
 }
 
-bool MultipartHandle::checkForBoundary(size_t& boundaryStartPosition, size_t& lastPartialMatchPosition)
+void CurlMultipartHandle::didReceiveData(const SharedBuffer& buffer)
 {
-    size_t contentLength = m_buffer.size();
-
-    boundaryStartPosition = notFound;
-    lastPartialMatchPosition = contentLength;
-
-    if (contentLength < m_boundaryLength)
-        return false;
-
-    const char* content = m_buffer.data();
-    size_t matched;
-
-    for (size_t i = 0; i < contentLength - m_boundaryLength; ++i) {
-        if (matchForBoundary(content, i, matched)) {
-            boundaryStartPosition = i;
-            return true;
-        }
+    if (m_state == State::End)
+        return; // The handler is closed down so ignore everything.
 
-        if (matched)
-            lastPartialMatchPosition = i;
-    }
+    m_buffer.append(buffer.data(), buffer.size());
 
-    return false;
+    while (processContent()) { }
 }
 
-bool MultipartHandle::parseHeadersIfPossible()
+void CurlMultipartHandle::didComplete()
 {
-    size_t contentLength = m_buffer.size();
-
-    if (!contentLength)
-        return false;
-
-    const char* content = m_buffer.data();
-
-    // Check if we have the header closing strings.
-    if (!strnstr(content, "\r\n\r\n", contentLength)) {
-        // Some servers closes the headers with only \n-s.
-        if (!strnstr(content, "\n\n", contentLength)) {
-            // Don't have the header closing string. Wait for more data.
-            return false;
-        }
-    }
-
-    // Parse the HTTP headers.
-    String value;
-    StringView name;
-    char* p = const_cast<char*>(content);
-    const char* end = content + contentLength;
-    size_t totalConsumedLength = 0;
-    for (; p < end; ++p) {
-        String failureReason;
-        size_t consumedLength = parseHTTPHeader(p, end - p, failureReason, name, value, false);
-        if (!consumedLength)
-            break; // No more header to parse.
-
-        p += consumedLength;
-        totalConsumedLength += consumedLength;
-
-        // The name should not be empty, but the value could be empty.
-        if (name.isEmpty())
-            break;
+    // Process the leftover data.
+    while (processContent()) { }
 
-        m_headers.add(name.toString(), value);
+    if (m_state != State::End) {
+        // It seems we are still not at the end of the processing.
+        // Push out the remaining data.
+        m_client.didReceiveDataFromMultipart(SharedBuffer::create(m_buffer.data(), m_buffer.size()));
+        m_state = State::End;
     }
 
-    m_buffer.remove(0, totalConsumedLength + 1);
-    return true;
-}
-
-void MultipartHandle::contentReceived(const char* data, size_t length)
-{
-    if (m_state == End)
-        return; // The handler is closed down so ignore everything.
-
-    m_buffer.append(data, length);
-
-    while (processContent()) { }
+    m_buffer.clear();
 }
 
-bool MultipartHandle::processContent()
+bool CurlMultipartHandle::processContent()
 {
 /*
     The allowed transitions between the states:
@@ -191,8 +162,8 @@ bool MultipartHandle::processContent()
 
 */
     switch (m_state) {
-    case CheckBoundary: {
-        if (m_buffer.size() < m_boundaryLength) {
+    case State::CheckBoundary: {
+        if (m_buffer.size() < m_boundary.length()) {
             // We don't have enough data, so just skip.
             return false;
         }
@@ -210,11 +181,11 @@ bool MultipartHandle::processContent()
 
         // Found the boundary start.
         // Consume everything before that and also the boundary
-        m_buffer.remove(0, boundaryStart + m_boundaryLength);
-        m_state = InBoundary;
+        m_buffer.remove(0, boundaryStart + m_boundary.length());
+        m_state = State::InBoundary;
     }
     // Fallthrough.
-    case InBoundary: {
+    case State::InBoundary: {
         // Now the first two characters should be: \r\n
         if (m_buffer.size() < 2)
             return false;
@@ -229,7 +200,7 @@ bool MultipartHandle::processContent()
             // So we'll check for a simple \n. Not really RFC compatible but servers do tricky things.
             if (content[0] != '\n') {
                 // Also no \n so just go to the end.
-                m_state = End;
+                m_state = State::End;
                 return false;
             }
 
@@ -240,41 +211,41 @@ bool MultipartHandle::processContent()
         // Consume the characters.
         m_buffer.remove(0, removeCount);
         m_headers.clear();
-        m_state = InHeader;
+        m_state = State::InHeader;
     }
     // Fallthrough.
-    case InHeader: {
+    case State::InHeader: {
         // Process the headers.
         if (!parseHeadersIfPossible()) {
             // Parsing of headers failed, try again later.
             return false;
         }
 
-        didReceiveResponse();
-        m_state = InContent;
+        m_client.didReceiveHeaderFromMultipart(m_headers);
+        m_state = State::InContent;
     }
     // Fallthrough.
-    case InContent: {
+    case State::InContent: {
         if (m_buffer.isEmpty())
             return false;
 
-        size_t boundaryStart = notFound;
+        size_t boundaryStart;
         size_t lastPartialMatch;
 
         if (!checkForBoundary(boundaryStart, lastPartialMatch) && boundaryStart == notFound) {
             // Did not find the boundary start, all data up to the lastPartialMatch is ok.
-            didReceiveData(lastPartialMatch);
+            m_client.didReceiveDataFromMultipart(SharedBuffer::create(m_buffer.data(), lastPartialMatch));
             m_buffer.remove(0, lastPartialMatch);
             return false;
         }
 
         // There was a boundary start (or end we'll check that later), push out part of the data.
-        didReceiveData(boundaryStart);
-        m_buffer.remove(0, boundaryStart + m_boundaryLength);
-        m_state = EndBoundary;
+        m_client.didReceiveDataFromMultipart(SharedBuffer::create(m_buffer.data(), boundaryStart));
+        m_buffer.remove(0, boundaryStart + m_boundary.length());
+        m_state = State::EndBoundary;
     }
     // Fallthrough.
-    case EndBoundary: {
+    case State::EndBoundary: {
         if (m_buffer.size() < 2)
             return false; // Not enough data to check. Return later when there is more data.
 
@@ -283,16 +254,16 @@ bool MultipartHandle::processContent()
 
         if (content[0] == '-' && content[1] == '-') {
             // This is a closing boundary. Close down the handler.
-            m_state = End;
+            m_state = State::End;
             return false;
         }
 
         // This was a simple content separator not a closing one.
         // Go to before the content processing.
-        m_state = InBoundary;
+        m_state = State::InBoundary;
         break;
     }
-    case End:
+    case State::End:
         // We are done. Nothing to do anymore.
         return false;
     default:
@@ -303,55 +274,85 @@ bool MultipartHandle::processContent()
     return true; // There are still things to process, so go for it.
 }
 
-void MultipartHandle::contentEnded()
+bool CurlMultipartHandle::checkForBoundary(size_t& boundaryStartPosition, size_t& lastPartialMatchPosition)
 {
-    // Process the leftover data.
-    while (processContent()) { }
+    auto contentLength = m_buffer.size();
 
-    if (m_state != End) {
-        // It seems we are still not at the end of the processing.
-        // Push out the remaining data.
-        didReceiveData(m_buffer.size());
-        m_state = End;
+    boundaryStartPosition = notFound;
+    lastPartialMatchPosition = contentLength;
+
+    if (contentLength < m_boundary.length())
+        return false;
+
+    const auto content = m_buffer.data();
+
+    for (size_t i = 0; i < contentLength - m_boundary.length(); ++i) {
+        auto length = matchedLength(content + i);
+        if (length == m_boundary.length()) {
+            boundaryStartPosition = i;
+            return true;
+        }
+
+        if (length)
+            lastPartialMatchPosition = i;
     }
 
-    m_buffer.clear();
+    return false;
 }
 
-void MultipartHandle::didReceiveData(size_t length)
+size_t CurlMultipartHandle::matchedLength(const char* data)
 {
-    ResourceHandleInternal* d = m_resourceHandle->getInternal();
-
-    if (!d->m_delegate->hasHandle()) {
-        // Request has been canceled, so we'll go to the end state.
-        m_state = End;
-        return;
+    auto length = m_boundary.length();
+    for (size_t i = 0; i < length; ++i) {
+        if (data[i] != m_boundary[i])
+            return i;
     }
 
-    if (d->client()) {
-        const char* data = m_buffer.data();
-        d->client()->didReceiveBuffer(m_resourceHandle, SharedBuffer::create(data, length), length);
-    }
+    return length;
 }
 
-void MultipartHandle::didReceiveResponse()
+bool CurlMultipartHandle::parseHeadersIfPossible()
 {
-    ResourceHandleInternal* d = m_resourceHandle->getInternal();
-    if (d->client()) {
-        auto response = d->m_response;
+    auto contentLength = m_buffer.size();
 
-        HTTPHeaderMap::const_iterator end = m_headers.end();
-        for (HTTPHeaderMap::const_iterator it = m_headers.begin(); it != end; ++it)
-            response.setHTTPHeaderField(it->key, it->value);
+    if (!contentLength)
+        return false;
 
-        String contentType = m_headers.get(HTTPHeaderName::ContentType);
-        String mimeType = extractMIMETypeFromMediaType(contentType);
+    const auto content = m_buffer.data();
 
-        response.setMimeType(mimeType.convertToASCIILowercase());
-        response.setTextEncodingName(extractCharsetFromMediaType(contentType));
+    // Check if we have the header closing strings.
+    if (!strnstr(content, "\r\n\r\n", contentLength)) {
+        // Some servers closes the headers with only \n-s.
+        if (!strnstr(content, "\n\n", contentLength)) {
+            // Don't have the header closing string. Wait for more data.
+            return false;
+        }
+    }
 
-        m_resourceHandle->didReceiveResponse(WTFMove(response));
+    // Parse the HTTP headers.
+    String value;
+    StringView name;
+    auto p = content;
+    const auto end = content + contentLength;
+    size_t totalConsumedLength = 0;
+    for (; p < end; ++p) {
+        String failureReason;
+        size_t consumedLength = parseHTTPHeader(p, end - p, failureReason, name, value, false);
+        if (!consumedLength)
+            break; // No more header to parse.
+
+        p += consumedLength;
+        totalConsumedLength += consumedLength;
+
+        // The name should not be empty, but the value could be empty.
+        if (name.isEmpty())
+            break;
+
+        m_headers.append(name.toString() + ": " + value + "\r\n");
     }
+
+    m_buffer.remove(0, totalConsumedLength + 1);
+    return true;
 }
 
 } // namespace WebCore
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2013 University of Szeged
+ * Copyright (C) 2018 Sony Interactive Entertainment Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
 
 #pragma once
 
-#include "HTTPHeaderMap.h"
-#include "ResourceHandle.h"
 #include <wtf/Vector.h>
 #include <wtf/text/WTFString.h>
 
 namespace WebCore {
 
-class MultipartHandle {
-public:
-    static bool extractBoundary(const String& contentType, String& boundary);
+class CurlMultipartHandleClient;
+class CurlResponse;
+class SharedBuffer;
 
-    MultipartHandle(ResourceHandle* handle, const String& boundary)
-        : m_resourceHandle(handle)
-        , m_boundary("--" + boundary)
-        , m_boundaryLength(m_boundary.length())
-        , m_state(CheckBoundary)
-    {
-    }
+class CurlMultipartHandle {
+public:
+    static std::unique_ptr<CurlMultipartHandle> createIfNeeded(CurlMultipartHandleClient&, const CurlResponse&);
 
-    ~MultipartHandle() = default;
+    CurlMultipartHandle(CurlMultipartHandleClient&, const String&);
+    ~CurlMultipartHandle() { }
 
-    void contentReceived(const char* data, size_t length);
-    void contentEnded();
+    void didReceiveData(const SharedBuffer&);
+    void didComplete();
 
 private:
-    enum MultipartHandleState {
+    enum class State {
         CheckBoundary,
         InBoundary,
         InHeader,
@@ -60,22 +56,21 @@ private:
         End
     };
 
-    void didReceiveData(size_t length);
-    void didReceiveResponse();
+    static std::optional<String> extractBoundary(const CurlResponse&);
+    static std::optional<String> extractBoundaryFromContentType(const String&);
 
-    bool matchForBoundary(const char* data, size_t position, size_t& matchedLength);
-    inline bool checkForBoundary(size_t& boundaryStartPosition, size_t& lastPartialMatchPosition);
-    bool parseHeadersIfPossible();
     bool processContent();
+    bool checkForBoundary(size_t& boundaryStartPosition, size_t& lastPartialMatchPosition);
+    size_t matchedLength(const char* data);
+    bool parseHeadersIfPossible();
 
-    ResourceHandle* m_resourceHandle;
-    String m_boundary;
-    size_t m_boundaryLength;
+    CurlMultipartHandleClient& m_client;
 
+    String m_boundary;
     Vector<char> m_buffer;
-    HTTPHeaderMap m_headers;
+    Vector<String> m_headers;
 
-    MultipartHandleState m_state;
+    State m_state { State::CheckBoundary };
 };
 
 } // namespace WebCore
diff --git a/Source/WebCore/platform/network/curl/CurlMultipartHandleClient.h b/Source/WebCore/platform/network/curl/CurlMultipartHandleClient.h
new file mode 100644 (file)
index 0000000..fd13fd3
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2018 Sony Interactive Entertainment Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+namespace WebCore {
+
+class CurlResponse;
+class SharedBuffer;
+
+class CurlMultipartHandleClient {
+public:
+    virtual void didReceiveHeaderFromMultipart(const Vector<String>&) = 0;
+    virtual void didReceiveDataFromMultipart(Ref<SharedBuffer>&&) = 0;
+
+protected:
+    ~CurlMultipartHandleClient() { }
+};
+
+} // namespace WebCore
index 25a3da1..786c6a3 100644 (file)
 
 namespace WebCore {
 
-CurlRequest::CurlRequest(const ResourceRequest&request, CurlRequestClient* client, bool shouldSuspend)
+CurlRequest::CurlRequest(const ResourceRequest&request, CurlRequestClient* client, bool shouldSuspend, bool enableMultipart)
     : m_request(request.isolatedCopy())
     , m_shouldSuspend(shouldSuspend)
+    , m_enableMultipart(enableMultipart)
     , m_formDataStream(m_request.httpBody())
 {
     ASSERT(isMainThread());
@@ -276,6 +277,7 @@ size_t CurlRequest::didReceiveHeader(String&& header)
     if (m_didReceiveResponse) {
         m_didReceiveResponse = false;
         m_response = CurlResponse { };
+        m_multipartHandle = nullptr;
     }
 
     auto receiveBytes = static_cast<size_t>(header.length());
@@ -314,6 +316,9 @@ size_t CurlRequest::didReceiveHeader(String&& header)
     if (auto metrics = m_curlHandle->getNetworkLoadMetrics())
         m_networkLoadMetrics = *metrics;
 
+    if (m_enableMultipart)
+        m_multipartHandle = CurlMultipartHandle::createIfNeeded(*this, m_response);
+
     // Response will send at didReceiveData() or didCompleteTransfer()
     // to receive continueDidRceiveResponse() for asynchronously.
 
@@ -331,13 +336,13 @@ size_t CurlRequest::didReceiveData(Ref<SharedBuffer>&& buffer)
         if (!m_isSyncRequest) {
             // For asynchronous, pause until completeDidReceiveResponse() is called.
             setCallbackPaused(true);
-            invokeDidReceiveResponse(Action::ReceiveData);
+            invokeDidReceiveResponse(m_response, Action::ReceiveData);
             return CURL_WRITEFUNC_PAUSE;
         }
 
         // For synchronous, completeDidReceiveResponse() is called in invokeDidReceiveResponse().
         // In this case, pause is unnecessary.
-        invokeDidReceiveResponse(Action::None);
+        invokeDidReceiveResponse(m_response, Action::None);
     }
 
     auto receiveBytes = buffer->size();
@@ -345,13 +350,47 @@ size_t CurlRequest::didReceiveData(Ref<SharedBuffer>&& buffer)
     writeDataToDownloadFileIfEnabled(buffer);
 
     if (receiveBytes) {
+        if (m_multipartHandle)
+            m_multipartHandle->didReceiveData(buffer);
+        else {
+            callClient([this, buffer = WTFMove(buffer)](CurlRequestClient* client) mutable {
+                if (client)
+                    client->curlDidReceiveBuffer(WTFMove(buffer));
+            });
+        }
+    }
+
+    return receiveBytes;
+}
+
+void CurlRequest::didReceiveHeaderFromMultipart(const Vector<String>& headers)
+{
+    if (isCompletedOrCancelled())
+        return;
+
+    CurlResponse response = m_response.isolatedCopy();
+    response.expectedContentLength = 0;
+    response.headers.clear();
+
+    for (auto header : headers)
+        response.headers.append(header);
+
+    invokeDidReceiveResponse(response, Action::None);
+}
+
+void CurlRequest::didReceiveDataFromMultipart(Ref<SharedBuffer>&& buffer)
+{
+    if (isCompletedOrCancelled())
+        return;
+
+    auto receiveBytes = buffer->size();
+
+    if (receiveBytes) {
         callClient([this, buffer = WTFMove(buffer)](CurlRequestClient* client) mutable {
             if (client)
                 client->curlDidReceiveBuffer(WTFMove(buffer));
         });
     }
-
-    return receiveBytes;
 }
 
 void CurlRequest::didCompleteTransfer(CURLcode result)
@@ -367,8 +406,11 @@ void CurlRequest::didCompleteTransfer(CURLcode result)
             // When completeDidReceiveResponse() is called, didCompleteTransfer() will be called again.
 
             m_finishedResultCode = result;
-            invokeDidReceiveResponse(Action::FinishTransfer);
+            invokeDidReceiveResponse(m_response, Action::FinishTransfer);
         } else {
+            if (m_multipartHandle)
+                m_multipartHandle->didComplete();
+
             if (auto metrics = m_curlHandle->getNetworkLoadMetrics())
                 m_networkLoadMetrics = *metrics;
 
@@ -401,6 +443,7 @@ void CurlRequest::finalizeTransfer()
 {
     closeDownloadFile();
     m_formDataStream.clean();
+    m_multipartHandle = nullptr;
     m_curlHandle = nullptr;
 }
 
@@ -469,22 +512,22 @@ void CurlRequest::invokeDidReceiveResponseForFile(URL& url)
     if (!m_isSyncRequest) {
         // DidReceiveResponse must not be called immediately
         CurlRequestScheduler::singleton().callOnWorkerThread([protectedThis = makeRef(*this)]() {
-            protectedThis->invokeDidReceiveResponse(Action::StartTransfer);
+            protectedThis->invokeDidReceiveResponse(protectedThis->m_response, Action::StartTransfer);
         });
     } else {
         // For synchronous, completeDidReceiveResponse() is called in platformContinueSynchronousDidReceiveResponse().
-        invokeDidReceiveResponse(Action::None);
+        invokeDidReceiveResponse(m_response, Action::None);
     }
 }
 
-void CurlRequest::invokeDidReceiveResponse(Action behaviorAfterInvoke)
+void CurlRequest::invokeDidReceiveResponse(const CurlResponse& response, Action behaviorAfterInvoke)
 {
-    ASSERT(!m_didNotifyResponse);
+    ASSERT(!m_didNotifyResponse || m_multipartHandle);
 
     m_didNotifyResponse = true;
     m_actionAfterInvoke = behaviorAfterInvoke;
 
-    callClient([this, response = m_response.isolatedCopy()](CurlRequestClient* client) {
+    callClient([this, response = response.isolatedCopy()](CurlRequestClient* client) {
         if (client)
             client->curlDidReceiveResponse(response);
     });
@@ -494,7 +537,7 @@ void CurlRequest::completeDidReceiveResponse()
 {
     ASSERT(isMainThread());
     ASSERT(m_didNotifyResponse);
-    ASSERT(!m_didReturnFromNotify);
+    ASSERT(!m_didReturnFromNotify || m_multipartHandle);
 
     if (isCancelled())
         return;
index ea34fae..b0118ec 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 Sony Interactive Entertainment Inc.
+ * Copyright (C) 2018 Sony Interactive Entertainment Inc.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -26,6 +26,8 @@
 #pragma once
 
 #include "CurlFormDataStream.h"
+#include "CurlMultipartHandle.h"
+#include "CurlMultipartHandleClient.h"
 #include "CurlRequestSchedulerClient.h"
 #include "CurlResponse.h"
 #include "CurlSSLVerifier.h"
@@ -40,13 +42,23 @@ class CurlRequestClient;
 class ResourceError;
 class SharedBuffer;
 
-class CurlRequest : public ThreadSafeRefCounted<CurlRequest>, public CurlRequestSchedulerClient {
+class CurlRequest : public ThreadSafeRefCounted<CurlRequest>, public CurlRequestSchedulerClient, public CurlMultipartHandleClient {
     WTF_MAKE_NONCOPYABLE(CurlRequest);
 
 public:
-    static Ref<CurlRequest> create(const ResourceRequest& request, CurlRequestClient* client, bool shouldSuspend = false)
+    enum class ShouldSuspend : bool {
+        No = false,
+        Yes = true
+    };
+
+    enum class EnableMultipart : bool {
+        No = false,
+        Yes = true
+    };
+
+    static Ref<CurlRequest> create(const ResourceRequest& request, CurlRequestClient* client, ShouldSuspend shouldSuspend = ShouldSuspend::No, EnableMultipart enableMultipart = EnableMultipart::No)
     {
-        return adoptRef(*new CurlRequest(request, client, shouldSuspend));
+        return adoptRef(*new CurlRequest(request, client, shouldSuspend == ShouldSuspend::Yes, enableMultipart == EnableMultipart::Yes));
     }
 
     virtual ~CurlRequest() = default;
@@ -82,7 +94,7 @@ private:
         FinishTransfer
     };
 
-    CurlRequest(const ResourceRequest&, CurlRequestClient*, bool shouldSuspend);
+    CurlRequest(const ResourceRequest&, CurlRequestClient*, bool shouldSuspend, bool enableMultipart);
 
     void retain() override { ref(); }
     void release() override { deref(); }
@@ -99,6 +111,8 @@ private:
     size_t willSendData(char*, size_t, size_t);
     size_t didReceiveHeader(String&&);
     size_t didReceiveData(Ref<SharedBuffer>&&);
+    void didReceiveHeaderFromMultipart(const Vector<String>&) override;
+    void didReceiveDataFromMultipart(Ref<SharedBuffer>&&) override;
     void didCompleteTransfer(CURLcode) override;
     void didCancelTransfer() override;
     void finalizeTransfer();
@@ -112,7 +126,7 @@ private:
     bool needToInvokeDidReceiveResponse() const { return !m_didNotifyResponse || !m_didReturnFromNotify; }
     bool needToInvokeDidCancelTransfer() const { return m_didNotifyResponse && !m_didReturnFromNotify && m_actionAfterInvoke == Action::FinishTransfer; }
     void invokeDidReceiveResponseForFile(URL&);
-    void invokeDidReceiveResponse(Action);
+    void invokeDidReceiveResponse(const CurlResponse&, Action);
     void setRequestPaused(bool);
     void setCallbackPaused(bool);
     void pausedStatusChanged();
@@ -139,10 +153,12 @@ private:
     String m_user;
     String m_password;
     bool m_shouldSuspend { false };
+    bool m_enableMultipart { false };
 
     std::unique_ptr<CurlHandle> m_curlHandle;
     CurlFormDataStream m_formDataStream;
     CurlSSLVerifier m_sslVerifier;
+    std::unique_ptr<CurlMultipartHandle> m_multipartHandle;
 
     CurlResponse m_response;
     bool m_didReceiveResponse { false };
index 732760c..84e1fda 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Copyright (C) 2004, 2006 Apple Inc.  All rights reserved.
  * Copyright (C) 2005, 2006 Michael Emmel mike.emmel@gmail.com
- * Copyright (C) 2017 Sony Interactive Entertainment Inc.
+ * Copyright (C) 2018 Sony Interactive Entertainment Inc.
  * All rights reserved.
  * Copyright (C) 2017 NAVER Corp. All rights reserved.
  *
@@ -37,7 +37,6 @@
 #include "CurlCacheManager.h"
 #include "CurlRequest.h"
 #include "HTTPParsers.h"
-#include "MultipartHandle.h"
 #include "ResourceHandleInternal.h"
 #include "SharedBuffer.h"
 #include "TextEncoding.h"
@@ -169,7 +168,8 @@ Ref<CurlRequest> ResourceHandleCurlDelegate::createCurlRequest(ResourceRequest&
         }
     }
 
-    return CurlRequest::create(request, this, m_defersLoading);
+    CurlRequest::ShouldSuspend shouldSuspend = m_defersLoading ? CurlRequest::ShouldSuspend::Yes : CurlRequest::ShouldSuspend::No;
+    return CurlRequest::create(request, this, shouldSuspend, CurlRequest::EnableMultipart::Yes);
 }
 
 bool ResourceHandleCurlDelegate::cancelledOrClientless()
@@ -193,13 +193,6 @@ void ResourceHandleCurlDelegate::curlDidReceiveResponse(const CurlResponse& rece
     if (m_curlRequest)
         m_handle->getInternal()->m_response.setDeprecatedNetworkLoadMetrics(m_curlRequest->getNetworkLoadMetrics());
 
-    if (response().isMultipart()) {
-        String boundary;
-        bool parsed = MultipartHandle::extractBoundary(response().httpHeaderField(HTTPHeaderName::ContentType), boundary);
-        if (parsed)
-            m_multipartHandle = std::make_unique<MultipartHandle>(m_handle, boundary);
-    }
-
     if (response().shouldRedirect()) {
         willSendRequest();
         return;
@@ -239,12 +232,8 @@ void ResourceHandleCurlDelegate::curlDidReceiveBuffer(Ref<SharedBuffer>&& buffer
     if (cancelledOrClientless())
         return;
 
-    if (m_multipartHandle)
-        m_multipartHandle->contentReceived(buffer->data(), buffer->size());
-    else if (m_handle->client()) {
-        CurlCacheManager::singleton().didReceiveData(*m_handle, buffer->data(), buffer->size());
-        m_handle->client()->didReceiveBuffer(m_handle, WTFMove(buffer), buffer->size());
-    }
+    CurlCacheManager::singleton().didReceiveData(*m_handle, buffer->data(), buffer->size());
+    m_handle->client()->didReceiveBuffer(m_handle, WTFMove(buffer), buffer->size());
 }
 
 void ResourceHandleCurlDelegate::curlDidComplete()
@@ -257,9 +246,6 @@ void ResourceHandleCurlDelegate::curlDidComplete()
     if (m_curlRequest)
         m_handle->getInternal()->m_response.setDeprecatedNetworkLoadMetrics(m_curlRequest->getNetworkLoadMetrics());
 
-    if (m_multipartHandle)
-        m_multipartHandle->contentEnded();
-
     if (m_handle->client()) {
         CurlCacheManager::singleton().didFinishLoading(*m_handle);
         m_handle->client()->didFinishLoading(m_handle);
index d492cd6..5ad8afe 100644 (file)
@@ -35,7 +35,6 @@
 namespace WebCore {
 
 class CurlRequest;
-class MultipartHandle;
 class ResourceHandle;
 class ResourceResponse;
 
@@ -84,7 +83,6 @@ private:
 
     // Used by main thread.
     ResourceHandle* m_handle;
-    std::unique_ptr<MultipartHandle> m_multipartHandle;
     unsigned m_authFailureCount { 0 };
     unsigned m_redirectCount { 0 };