| /* |
| * Copyright (C) 2017-2018 Apple Inc. All rights reserved. |
| * |
| * 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. |
| */ |
| |
| #include "config.h" |
| #include "SocketStreamHandleImpl.h" |
| |
| #if !USE(SOUP) |
| |
| #include "CookieRequestHeaderFieldProxy.h" |
| #include "NetworkStorageSession.h" |
| #include "SocketStreamHandleClient.h" |
| #include "StorageSessionProvider.h" |
| #include <wtf/Function.h> |
| |
| namespace WebCore { |
| |
| void SocketStreamHandleImpl::platformSend(const uint8_t* data, size_t length, Function<void(bool)>&& completionHandler) |
| { |
| if (!m_buffer.isEmpty()) { |
| if (m_buffer.size() + length > maxBufferSize) { |
| // FIXME: report error to indicate that buffer has no more space. |
| return completionHandler(false); |
| } |
| m_buffer.append(data, length); |
| m_client.didUpdateBufferedAmount(*this, bufferedAmount()); |
| return completionHandler(true); |
| } |
| size_t bytesWritten = 0; |
| if (m_state == Open) { |
| if (auto result = platformSendInternal(data, length)) |
| bytesWritten = result.value(); |
| else |
| return completionHandler(false); |
| } |
| if (m_buffer.size() + length - bytesWritten > maxBufferSize) { |
| // FIXME: report error to indicate that buffer has no more space. |
| return completionHandler(false); |
| } |
| if (bytesWritten < length) { |
| m_buffer.append(data + bytesWritten, length - bytesWritten); |
| m_client.didUpdateBufferedAmount(static_cast<SocketStreamHandle&>(*this), bufferedAmount()); |
| } |
| return completionHandler(true); |
| } |
| |
| static size_t removeTerminationCharacters(const uint8_t* data, size_t dataLength) |
| { |
| #ifndef NDEBUG |
| ASSERT(dataLength > 2); |
| ASSERT(data[dataLength - 2] == '\r'); |
| ASSERT(data[dataLength - 1] == '\n'); |
| #else |
| UNUSED_PARAM(data); |
| #endif |
| |
| // Remove the terminating '\r\n' |
| return dataLength - 2; |
| } |
| |
| static Optional<std::pair<Vector<uint8_t>, bool>> cookieDataForHandshake(const NetworkStorageSession* networkStorageSession, const CookieRequestHeaderFieldProxy& headerFieldProxy) |
| { |
| if (!networkStorageSession) |
| return WTF::nullopt; |
| |
| auto [cookieDataString, secureCookiesAccessed] = networkStorageSession->cookieRequestHeaderFieldValue(headerFieldProxy); |
| if (cookieDataString.isEmpty()) |
| return std::pair<Vector<uint8_t>, bool> { { }, secureCookiesAccessed }; |
| |
| CString cookieData = cookieDataString.utf8(); |
| |
| Vector<uint8_t> data = { 'C', 'o', 'o', 'k', 'i', 'e', ':', ' ' }; |
| data.append(cookieData.data(), cookieData.length()); |
| data.appendVector(Vector<uint8_t>({ '\r', '\n', '\r', '\n' })); |
| |
| return std::pair<Vector<uint8_t>, bool> { data, secureCookiesAccessed }; |
| } |
| |
| void SocketStreamHandleImpl::platformSendHandshake(const uint8_t* data, size_t length, const Optional<CookieRequestHeaderFieldProxy>& headerFieldProxy, Function<void(bool, bool)>&& completionHandler) |
| { |
| Vector<uint8_t> cookieData; |
| bool secureCookiesAccessed = false; |
| |
| if (headerFieldProxy) { |
| auto cookieDataFromNetworkSession = cookieDataForHandshake(m_storageSessionProvider ? m_storageSessionProvider->storageSession() : nullptr, *headerFieldProxy); |
| if (!cookieDataFromNetworkSession) { |
| completionHandler(false, false); |
| return; |
| } |
| |
| std::tie(cookieData, secureCookiesAccessed) = *cookieDataFromNetworkSession; |
| if (cookieData.size()) |
| length = removeTerminationCharacters(data, length); |
| } |
| |
| if (!m_buffer.isEmpty()) { |
| if (m_buffer.size() + length + cookieData.size() > maxBufferSize) { |
| // FIXME: report error to indicate that buffer has no more space. |
| return completionHandler(false, secureCookiesAccessed); |
| } |
| m_buffer.append(data, length); |
| m_buffer.append(cookieData.data(), cookieData.size()); |
| m_client.didUpdateBufferedAmount(*this, bufferedAmount()); |
| return completionHandler(true, secureCookiesAccessed); |
| } |
| size_t bytesWritten = 0; |
| if (m_state == Open) { |
| // Unfortunately, we need to send the data in one buffer or else the handshake fails. |
| Vector<uint8_t> sendData; |
| sendData.reserveCapacity(length + cookieData.size()); |
| sendData.append(data, length); |
| sendData.append(cookieData.data(), cookieData.size()); |
| |
| if (auto result = platformSendInternal(sendData.data(), sendData.size())) |
| bytesWritten = result.value(); |
| else |
| return completionHandler(false, secureCookiesAccessed); |
| } |
| if (m_buffer.size() + length + cookieData.size() - bytesWritten > maxBufferSize) { |
| // FIXME: report error to indicate that buffer has no more space. |
| return completionHandler(false, secureCookiesAccessed); |
| } |
| if (bytesWritten < length + cookieData.size()) { |
| size_t cookieBytesWritten = 0; |
| if (bytesWritten < length) |
| m_buffer.append(data + bytesWritten, length - bytesWritten); |
| else |
| cookieBytesWritten = bytesWritten - length; |
| m_buffer.append(cookieData.data() + cookieBytesWritten, cookieData.size() - cookieBytesWritten); |
| m_client.didUpdateBufferedAmount(static_cast<SocketStreamHandle&>(*this), bufferedAmount()); |
| } |
| return completionHandler(true, secureCookiesAccessed); |
| } |
| |
| bool SocketStreamHandleImpl::sendPendingData() |
| { |
| if (m_state != Open && m_state != Closing) |
| return false; |
| if (m_buffer.isEmpty()) { |
| if (m_state == Open) |
| return false; |
| if (m_state == Closing) { |
| disconnect(); |
| return false; |
| } |
| } |
| bool pending; |
| do { |
| auto result = platformSendInternal(m_buffer.firstBlockData(), m_buffer.firstBlockSize()); |
| if (!result) |
| return false; |
| size_t bytesWritten = result.value(); |
| if (!bytesWritten) |
| return false; |
| pending = bytesWritten != m_buffer.firstBlockSize(); |
| ASSERT(m_buffer.size() - bytesWritten <= maxBufferSize); |
| m_buffer.consume(bytesWritten); |
| } while (!pending && !m_buffer.isEmpty()); |
| m_client.didUpdateBufferedAmount(static_cast<SocketStreamHandle&>(*this), bufferedAmount()); |
| return true; |
| } |
| |
| size_t SocketStreamHandleImpl::bufferedAmount() |
| { |
| return m_buffer.size(); |
| } |
| |
| } // namespace WebCore |
| |
| #endif // !USE(SOUP) |