nr_socket_tcp.cpp (8431B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set sw=2 ts=8 et tw=80 ft=cpp : */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 5 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 /* 8 Copyright (c) 2007, Adobe Systems, Incorporated 9 Copyright (c) 2013, Mozilla 10 11 All rights reserved. 12 13 Redistribution and use in source and binary forms, with or without 14 modification, are permitted provided that the following conditions are 15 met: 16 17 * Redistributions of source code must retain the above copyright 18 notice, this list of conditions and the following disclaimer. 19 20 * Redistributions in binary form must reproduce the above copyright 21 notice, this list of conditions and the following disclaimer in the 22 documentation and/or other materials provided with the distribution. 23 24 * Neither the name of Adobe Systems, Network Resonance, Mozilla nor 25 the names of its contributors may be used to endorse or promote 26 products derived from this software without specific prior written 27 permission. 28 29 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 30 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 31 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 32 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 33 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 34 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 35 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 36 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 37 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 38 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 39 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 40 */ 41 42 #include "nr_socket_tcp.h" 43 44 #include "WebrtcTCPSocketWrapper.h" 45 #include "mozilla/ErrorNames.h" 46 47 namespace mozilla { 48 using namespace net; 49 50 using std::shared_ptr; 51 52 class NrTcpSocketData { 53 public: 54 explicit NrTcpSocketData(nsTArray<uint8_t>&& aData) 55 : mData(std::move(aData)) { 56 MOZ_COUNT_CTOR(NrTcpSocketData); 57 } 58 59 MOZ_COUNTED_DTOR(NrTcpSocketData) 60 61 const nsTArray<uint8_t>& GetData() const { return mData; } 62 63 private: 64 nsTArray<uint8_t> mData; 65 }; 66 67 NrTcpSocket::NrTcpSocket(const shared_ptr<NrSocketProxyConfig>& aConfig) 68 : mClosed(false), 69 mReadOffset(0), 70 mConfig(aConfig), 71 mWebrtcTCPSocket(nullptr) { 72 r_log(LOG_GENERIC, LOG_DEBUG, "NrTcpSocket::NrTcpSocket %p\n", this); 73 } 74 75 NrTcpSocket::~NrTcpSocket() { 76 r_log(LOG_GENERIC, LOG_DEBUG, "NrTcpSocket::~NrTcpSocket %p\n", this); 77 MOZ_ASSERT(!mWebrtcTCPSocket, "webrtc TCP socket not null"); 78 } 79 80 int NrTcpSocket::create(nr_transport_addr* aAddr) { 81 r_log(LOG_GENERIC, LOG_DEBUG, "NrTcpSocket::create %p\n", this); 82 int32_t port; 83 nsCString host; 84 85 // Sanity check 86 if (nr_transport_addr_get_addrstring_and_port(aAddr, &host, &port)) { 87 return R_FAILED; 88 } 89 90 if (nr_transport_addr_copy(&my_addr_, aAddr)) { 91 return R_FAILED; 92 } 93 94 return 0; 95 } 96 97 int NrTcpSocket::connect(const nr_transport_addr* aAddr) { 98 r_log(LOG_GENERIC, LOG_DEBUG, "NrTcpSocket::connect %p\n", this); 99 100 nsCString remote_host; 101 int remote_port; 102 103 if (NS_WARN_IF(nr_transport_addr_get_addrstring_and_port(aAddr, &remote_host, 104 &remote_port))) { 105 return R_FAILED; 106 } 107 108 bool use_tls = aAddr->tls; 109 110 nsCString local_addr; 111 int local_port; 112 113 if (NS_WARN_IF(nr_transport_addr_get_addrstring_and_port( 114 &my_addr_, &local_addr, &local_port))) { 115 return R_FAILED; 116 } 117 118 mWebrtcTCPSocket = new WebrtcTCPSocketWrapper(this); 119 120 mWebrtcTCPSocket->AsyncOpen(remote_host, remote_port, local_addr, local_port, 121 use_tls, mConfig); 122 123 // trigger nr_socket_buffered to set write/read callback 124 return R_WOULDBLOCK; 125 } 126 127 void NrTcpSocket::close() { 128 r_log(LOG_GENERIC, LOG_DEBUG, "NrTcpSocket::close %p\n", this); 129 130 if (mClosed) { 131 return; 132 } 133 134 mClosed = true; 135 136 // We're not always open at this point. 137 if (mWebrtcTCPSocket) { 138 mWebrtcTCPSocket->Close(); 139 mWebrtcTCPSocket = nullptr; 140 } 141 } 142 143 int NrTcpSocket::write(const void* aBuffer, size_t aCount, size_t* aWrote) { 144 r_log(LOG_GENERIC, LOG_DEBUG, "NrTcpSocket::write %p count=%zu\n", this, 145 aCount); 146 147 if (mClosed) { 148 return R_FAILED; 149 } 150 151 if (!aWrote) { 152 return R_FAILED; 153 } 154 155 if (NS_WARN_IF(!mWebrtcTCPSocket)) { 156 return R_FAILED; 157 } 158 159 *aWrote = aCount; 160 161 if (aCount > 0) { 162 nsTArray<uint8_t> writeData; 163 writeData.SetLength(aCount); 164 memcpy(writeData.Elements(), aBuffer, aCount); 165 166 mWebrtcTCPSocket->SendWrite(std::move(writeData)); 167 } 168 169 return 0; 170 } 171 172 int NrTcpSocket::read(void* aBuffer, size_t aCount, size_t* aRead) { 173 r_log(LOG_GENERIC, LOG_DEBUG, "NrTcpSocket::read %p\n", this); 174 175 if (mClosed) { 176 return R_FAILED; 177 } 178 179 if (!aRead) { 180 return R_FAILED; 181 } 182 183 *aRead = 0; 184 185 if (mReadQueue.empty()) { 186 return R_WOULDBLOCK; 187 } 188 189 while (aCount > 0 && !mReadQueue.empty()) { 190 const NrTcpSocketData& data = mReadQueue.front(); 191 192 size_t remainingCount = data.GetData().Length() - mReadOffset; 193 size_t amountToCopy = std::min(aCount, remainingCount); 194 195 char* buffer = static_cast<char*>(aBuffer) + (*aRead); 196 197 memcpy(buffer, data.GetData().Elements() + mReadOffset, amountToCopy); 198 199 mReadOffset += amountToCopy; 200 *aRead += amountToCopy; 201 aCount -= amountToCopy; 202 203 if (remainingCount == amountToCopy) { 204 mReadOffset = 0; 205 mReadQueue.pop_front(); 206 } 207 } 208 209 return 0; 210 } 211 212 int NrTcpSocket::getaddr(nr_transport_addr* aAddr) { 213 r_log(LOG_GENERIC, LOG_DEBUG, "NrTcpSocket::getaddr %p\n", this); 214 return nr_transport_addr_copy(aAddr, &my_addr_); 215 } 216 217 int NrTcpSocket::sendto(const void* aBuffer, size_t aCount, int aFlags, 218 const nr_transport_addr* aAddr) { 219 // never call this 220 MOZ_ASSERT(0); 221 return R_FAILED; 222 } 223 224 int NrTcpSocket::recvfrom(void* aBuffer, size_t aCount, size_t* aRead, 225 int aFlags, nr_transport_addr* aAddr) { 226 // never call this 227 MOZ_ASSERT(0); 228 return R_FAILED; 229 } 230 231 int NrTcpSocket::listen(int aBacklog) { 232 r_log(LOG_GENERIC, LOG_DEBUG, "NrTcpSocket::listen %p\n", this); 233 return R_INTERNAL; 234 } 235 236 int NrTcpSocket::accept(nr_transport_addr* aAddr, nr_socket** aSocket) { 237 r_log(LOG_GENERIC, LOG_DEBUG, "NrTcpSocket::accept %p\n", this); 238 return R_INTERNAL; 239 } 240 241 // WebrtcTCPSocketCallback 242 void NrTcpSocket::OnClose(nsresult aReason) { 243 nsCString errorName; 244 GetErrorName(aReason, errorName); 245 246 r_log(LOG_GENERIC, LOG_ERR, "NrTcpSocket::OnClose %p reason=%u name=%s\n", 247 this, static_cast<uint32_t>(aReason), errorName.get()); 248 249 close(); 250 251 DoCallbacks(); 252 } 253 254 void NrTcpSocket::OnConnected(const nsACString& aProxyType) { 255 r_log(LOG_GENERIC, LOG_DEBUG, "NrTcpSocket::OnConnected %p\n", this); 256 if (aProxyType != "" && aProxyType != "direct") { 257 my_addr_.is_proxied = true; 258 } 259 260 DoCallbacks(); 261 } 262 263 void NrTcpSocket::OnRead(nsTArray<uint8_t>&& aReadData) { 264 r_log(LOG_GENERIC, LOG_DEBUG, "NrTcpSocket::OnRead %p read=%zu\n", this, 265 aReadData.Length()); 266 267 mReadQueue.emplace_back(std::move(aReadData)); 268 269 DoCallbacks(); 270 } 271 272 void NrTcpSocket::DoCallbacks() { 273 size_t lastCount = -1; 274 size_t currentCount = 0; 275 while ((poll_flags() & PR_POLL_READ) != 0 && 276 // Make sure whatever is reading knows we're closed. This doesn't need 277 // to happen for writes since ICE doesn't like a failing writable. 278 (mClosed || (currentCount = CountUnreadBytes()) > 0) && 279 lastCount != currentCount) { 280 fire_callback(NR_ASYNC_WAIT_READ); 281 lastCount = currentCount; 282 } 283 284 // We're always ready to write after we're connected. The parent process will 285 // buffer writes for us. 286 if (!mClosed && mWebrtcTCPSocket && (poll_flags() & PR_POLL_WRITE) != 0) { 287 fire_callback(NR_ASYNC_WAIT_WRITE); 288 } 289 } 290 291 size_t NrTcpSocket::CountUnreadBytes() const { 292 size_t count = 0; 293 294 for (const NrTcpSocketData& data : mReadQueue) { 295 count += data.GetData().Length(); 296 } 297 298 MOZ_ASSERT(count >= mReadOffset, "offset exceeds read buffer length"); 299 300 count -= mReadOffset; 301 302 return count; 303 } 304 305 void NrTcpSocket::AssignChannel_DoNotUse(WebrtcTCPSocketWrapper* aWrapper) { 306 mWebrtcTCPSocket = aWrapper; 307 } 308 309 } // namespace mozilla