1/*
2 * Copyright (C) 2017-2018 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 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "SocketStreamHandleImpl.h"
28
29#include "CookieRequestHeaderFieldProxy.h"
30#include "NetworkStorageSession.h"
31#include "SocketStreamHandleClient.h"
32#include "StorageSessionProvider.h"
33#include <wtf/Function.h>
34
35namespace WebCore {
36
37void SocketStreamHandleImpl::platformSend(const uint8_t* data, size_t length, Function<void(bool)>&& completionHandler)
38{
39 if (!m_buffer.isEmpty()) {
40 if (m_buffer.size() + length > maxBufferSize) {
41 // FIXME: report error to indicate that buffer has no more space.
42 return completionHandler(false);
43 }
44 m_buffer.append(data, length);
45 m_client.didUpdateBufferedAmount(*this, bufferedAmount());
46 return completionHandler(true);
47 }
48 size_t bytesWritten = 0;
49 if (m_state == Open) {
50 if (auto result = platformSendInternal(data, length))
51 bytesWritten = result.value();
52 else
53 return completionHandler(false);
54 }
55 if (m_buffer.size() + length - bytesWritten > maxBufferSize) {
56 // FIXME: report error to indicate that buffer has no more space.
57 return completionHandler(false);
58 }
59 if (bytesWritten < length) {
60 m_buffer.append(data + bytesWritten, length - bytesWritten);
61 m_client.didUpdateBufferedAmount(static_cast<SocketStreamHandle&>(*this), bufferedAmount());
62 }
63 return completionHandler(true);
64}
65
66static size_t removeTerminationCharacters(const uint8_t* data, size_t dataLength)
67{
68#ifndef NDEBUG
69 ASSERT(dataLength > 2);
70 ASSERT(data[dataLength - 2] == '\r');
71 ASSERT(data[dataLength - 1] == '\n');
72#else
73 UNUSED_PARAM(data);
74#endif
75
76 // Remove the terminating '\r\n'
77 return dataLength - 2;
78}
79
80static Optional<std::pair<Vector<uint8_t>, bool>> cookieDataForHandshake(const NetworkStorageSession* networkStorageSession, const CookieRequestHeaderFieldProxy& headerFieldProxy)
81{
82 if (!networkStorageSession)
83 return WTF::nullopt;
84
85 String cookieDataString;
86 bool secureCookiesAccessed = false;
87 std::tie(cookieDataString, secureCookiesAccessed) = networkStorageSession->cookieRequestHeaderFieldValue(headerFieldProxy);
88 if (cookieDataString.isEmpty())
89 return std::pair<Vector<uint8_t>, bool> { { }, secureCookiesAccessed };
90
91 CString cookieData = cookieDataString.utf8();
92
93 Vector<uint8_t> data = { 'C', 'o', 'o', 'k', 'i', 'e', ':', ' ' };
94 data.append(cookieData.data(), cookieData.length());
95 data.appendVector(Vector<uint8_t>({ '\r', '\n', '\r', '\n' }));
96
97 return std::pair<Vector<uint8_t>, bool> { data, secureCookiesAccessed };
98}
99
100void SocketStreamHandleImpl::platformSendHandshake(const uint8_t* data, size_t length, const Optional<CookieRequestHeaderFieldProxy>& headerFieldProxy, Function<void(bool, bool)>&& completionHandler)
101{
102 Vector<uint8_t> cookieData;
103 bool secureCookiesAccessed = false;
104
105 if (headerFieldProxy) {
106 auto cookieDataFromNetworkSession = cookieDataForHandshake(m_storageSessionProvider ? m_storageSessionProvider->storageSession() : nullptr, *headerFieldProxy);
107 if (!cookieDataFromNetworkSession) {
108 completionHandler(false, false);
109 return;
110 }
111
112 std::tie(cookieData, secureCookiesAccessed) = *cookieDataFromNetworkSession;
113 if (cookieData.size())
114 length = removeTerminationCharacters(data, length);
115 }
116
117 if (!m_buffer.isEmpty()) {
118 if (m_buffer.size() + length + cookieData.size() > maxBufferSize) {
119 // FIXME: report error to indicate that buffer has no more space.
120 return completionHandler(false, secureCookiesAccessed);
121 }
122 m_buffer.append(data, length);
123 m_buffer.append(cookieData.data(), cookieData.size());
124 m_client.didUpdateBufferedAmount(*this, bufferedAmount());
125 return completionHandler(true, secureCookiesAccessed);
126 }
127 size_t bytesWritten = 0;
128 if (m_state == Open) {
129 // Unfortunately, we need to send the data in one buffer or else the handshake fails.
130 Vector<uint8_t> sendData;
131 sendData.reserveCapacity(length + cookieData.size());
132 sendData.append(data, length);
133 sendData.append(cookieData.data(), cookieData.size());
134
135 if (auto result = platformSendInternal(sendData.data(), sendData.size()))
136 bytesWritten = result.value();
137 else
138 return completionHandler(false, secureCookiesAccessed);
139 }
140 if (m_buffer.size() + length + cookieData.size() - bytesWritten > maxBufferSize) {
141 // FIXME: report error to indicate that buffer has no more space.
142 return completionHandler(false, secureCookiesAccessed);
143 }
144 if (bytesWritten < length + cookieData.size()) {
145 size_t cookieBytesWritten = 0;
146 if (bytesWritten < length)
147 m_buffer.append(data + bytesWritten, length - bytesWritten);
148 else
149 cookieBytesWritten = bytesWritten - length;
150 m_buffer.append(cookieData.data() + cookieBytesWritten, cookieData.size() - cookieBytesWritten);
151 m_client.didUpdateBufferedAmount(static_cast<SocketStreamHandle&>(*this), bufferedAmount());
152 }
153 return completionHandler(true, secureCookiesAccessed);
154}
155
156bool SocketStreamHandleImpl::sendPendingData()
157{
158 if (m_state != Open && m_state != Closing)
159 return false;
160 if (m_buffer.isEmpty()) {
161 if (m_state == Open)
162 return false;
163 if (m_state == Closing) {
164 disconnect();
165 return false;
166 }
167 }
168 bool pending;
169 do {
170 auto result = platformSendInternal(m_buffer.firstBlockData(), m_buffer.firstBlockSize());
171 if (!result)
172 return false;
173 size_t bytesWritten = result.value();
174 if (!bytesWritten)
175 return false;
176 pending = bytesWritten != m_buffer.firstBlockSize();
177 ASSERT(m_buffer.size() - bytesWritten <= maxBufferSize);
178 m_buffer.consume(bytesWritten);
179 } while (!pending && !m_buffer.isEmpty());
180 m_client.didUpdateBufferedAmount(static_cast<SocketStreamHandle&>(*this), bufferedAmount());
181 return true;
182}
183
184size_t SocketStreamHandleImpl::bufferedAmount()
185{
186 return m_buffer.size();
187}
188
189} // namespace WebCore
190