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 | |
35 | namespace WebCore { |
36 | |
37 | void 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 | |
66 | static 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 | |
80 | static Optional<std::pair<Vector<uint8_t>, bool>> cookieDataForHandshake(const NetworkStorageSession* networkStorageSession, const CookieRequestHeaderFieldProxy& ) |
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 | |
100 | void SocketStreamHandleImpl::platformSendHandshake(const uint8_t* data, size_t length, const Optional<CookieRequestHeaderFieldProxy>& , 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 | |
156 | bool 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 | |
184 | size_t SocketStreamHandleImpl::bufferedAmount() |
185 | { |
186 | return m_buffer.size(); |
187 | } |
188 | |
189 | } // namespace WebCore |
190 | |