WebSocketConnection.cpp (7471B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set sw=2 ts=8 et tw=80 : */ 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 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "nsIInterfaceRequestor.h" 8 #include "WebSocketConnection.h" 9 10 #include "WebSocketLog.h" 11 #include "mozilla/net/WebSocketConnectionListener.h" 12 #include "mozilla/Components.h" 13 #include "nsIOService.h" 14 #include "nsITLSSocketControl.h" 15 #include "nsISocketTransport.h" 16 #include "nsITransportSecurityInfo.h" 17 #include "nsSocketTransportService2.h" 18 19 namespace mozilla::net { 20 21 NS_IMPL_ISUPPORTS(WebSocketConnection, nsIInputStreamCallback, 22 nsIOutputStreamCallback) 23 24 WebSocketConnection::WebSocketConnection(nsISocketTransport* aTransport, 25 nsIAsyncInputStream* aInputStream, 26 nsIAsyncOutputStream* aOutputStream) 27 : mTransport(aTransport), 28 mSocketIn(aInputStream), 29 mSocketOut(aOutputStream) { 30 LOG(("WebSocketConnection ctor %p\n", this)); 31 } 32 33 WebSocketConnection::~WebSocketConnection() { 34 LOG(("WebSocketConnection dtor %p\n", this)); 35 } 36 37 nsresult WebSocketConnection::Init(WebSocketConnectionListener* aListener) { 38 NS_ENSURE_ARG_POINTER(aListener); 39 40 mListener = aListener; 41 nsresult rv; 42 mSocketThread = mozilla::components::SocketTransport::Service(&rv); 43 if (NS_FAILED(rv)) { 44 return rv; 45 } 46 47 if (!mTransport) { 48 return NS_ERROR_FAILURE; 49 } 50 51 if (XRE_IsParentProcess()) { 52 nsCOMPtr<nsIInterfaceRequestor> callbacks = do_QueryInterface(mListener); 53 mTransport->SetSecurityCallbacks(callbacks); 54 } else { 55 // NOTE: we don't use security callbacks in socket process. 56 mTransport->SetSecurityCallbacks(nullptr); 57 } 58 return mTransport->SetEventSink(nullptr, nullptr); 59 } 60 61 void WebSocketConnection::GetIoTarget(nsIEventTarget** aTarget) { 62 nsCOMPtr<nsIEventTarget> target = mSocketThread; 63 return target.forget(aTarget); 64 } 65 66 void WebSocketConnection::Close() { 67 LOG(("WebSocketConnection::Close %p\n", this)); 68 MOZ_ASSERT(OnSocketThread()); 69 70 if (mTransport) { 71 mTransport->SetSecurityCallbacks(nullptr); 72 mTransport->SetEventSink(nullptr, nullptr); 73 mTransport->Close(NS_BASE_STREAM_CLOSED); 74 mTransport = nullptr; 75 } 76 77 if (mSocketIn) { 78 if (mStartReadingCalled) { 79 mSocketIn->AsyncWait(nullptr, 0, 0, nullptr); 80 } 81 mSocketIn = nullptr; 82 } 83 84 if (mSocketOut) { 85 mSocketOut->AsyncWait(nullptr, 0, 0, nullptr); 86 mSocketOut = nullptr; 87 } 88 } 89 90 nsresult WebSocketConnection::WriteOutputData(nsTArray<uint8_t>&& aData) { 91 MOZ_ASSERT(OnSocketThread()); 92 93 if (!mSocketOut) { 94 return NS_ERROR_NOT_AVAILABLE; 95 } 96 97 mOutputQueue.emplace_back(std::move(aData)); 98 return OnOutputStreamReady(mSocketOut); 99 } 100 101 nsresult WebSocketConnection::WriteOutputData(const uint8_t* aHdrBuf, 102 uint32_t aHdrBufLength, 103 const uint8_t* aPayloadBuf, 104 uint32_t aPayloadBufLength) { 105 MOZ_ASSERT_UNREACHABLE("Should not be called"); 106 return NS_ERROR_NOT_IMPLEMENTED; 107 } 108 109 nsresult WebSocketConnection::StartReading() { 110 MOZ_ASSERT(OnSocketThread()); 111 112 if (!mSocketIn) { 113 return NS_ERROR_NOT_AVAILABLE; 114 } 115 116 MOZ_ASSERT(!mStartReadingCalled, "StartReading twice"); 117 mStartReadingCalled = true; 118 return mSocketIn->AsyncWait(this, 0, 0, mSocketThread); 119 } 120 121 void WebSocketConnection::DrainSocketData() { 122 MOZ_ASSERT(OnSocketThread()); 123 124 if (!mSocketIn || !mListener) { 125 return; 126 } 127 128 // If we leave any data unconsumed (including the tcp fin) a RST will be 129 // generated The right thing to do here is shutdown(SHUT_WR) and then wait a 130 // little while to see if any data comes in.. but there is no reason to delay 131 // things for that when the websocket handshake is supposed to guarantee a 132 // quiet connection except for that fin. 133 char buffer[512]; 134 uint32_t count = 0; 135 uint32_t total = 0; 136 nsresult rv; 137 do { 138 total += count; 139 rv = mSocketIn->Read(buffer, 512, &count); 140 if (rv != NS_BASE_STREAM_WOULD_BLOCK && (NS_FAILED(rv) || count == 0)) { 141 mListener->OnTCPClosed(); 142 } 143 } while (NS_SUCCEEDED(rv) && count > 0 && total < 32000); 144 } 145 146 nsresult WebSocketConnection::GetSecurityInfo( 147 nsITransportSecurityInfo** aSecurityInfo) { 148 LOG(("WebSocketConnection::GetSecurityInfo() %p\n", this)); 149 MOZ_ASSERT(OnSocketThread()); 150 *aSecurityInfo = nullptr; 151 152 if (mTransport) { 153 nsCOMPtr<nsITLSSocketControl> tlsSocketControl; 154 nsresult rv = 155 mTransport->GetTlsSocketControl(getter_AddRefs(tlsSocketControl)); 156 if (NS_FAILED(rv)) { 157 return rv; 158 } 159 nsCOMPtr<nsITransportSecurityInfo> securityInfo( 160 do_QueryInterface(tlsSocketControl)); 161 if (securityInfo) { 162 securityInfo.forget(aSecurityInfo); 163 } 164 } 165 return NS_OK; 166 } 167 168 NS_IMETHODIMP 169 WebSocketConnection::OnInputStreamReady(nsIAsyncInputStream* aStream) { 170 LOG(("WebSocketConnection::OnInputStreamReady() %p\n", this)); 171 MOZ_ASSERT(OnSocketThread()); 172 MOZ_ASSERT(mListener); 173 174 // did we we clean up the socket after scheduling InputReady? 175 if (!mSocketIn) { 176 return NS_OK; 177 } 178 179 // this is after the http upgrade - so we are speaking websockets 180 uint8_t buffer[2048]; 181 uint32_t count; 182 nsresult rv; 183 184 do { 185 rv = mSocketIn->Read((char*)buffer, 2048, &count); 186 LOG(("WebSocketConnection::OnInputStreamReady: read %u rv %" PRIx32 "\n", 187 count, static_cast<uint32_t>(rv))); 188 189 if (rv == NS_BASE_STREAM_WOULD_BLOCK) { 190 mSocketIn->AsyncWait(this, 0, 0, mSocketThread); 191 return NS_OK; 192 } 193 194 if (NS_FAILED(rv)) { 195 mListener->OnError(rv); 196 return rv; 197 } 198 199 if (count == 0) { 200 mListener->OnError(NS_BASE_STREAM_CLOSED); 201 return NS_OK; 202 } 203 204 rv = mListener->OnDataReceived(buffer, count); 205 if (NS_FAILED(rv)) { 206 mListener->OnError(rv); 207 return rv; 208 } 209 } while (NS_SUCCEEDED(rv) && mSocketIn && mListener); 210 211 return NS_OK; 212 } 213 214 NS_IMETHODIMP 215 WebSocketConnection::OnOutputStreamReady(nsIAsyncOutputStream* aStream) { 216 LOG(("WebSocketConnection::OnOutputStreamReady() %p\n", this)); 217 MOZ_ASSERT(OnSocketThread()); 218 MOZ_ASSERT(mListener); 219 220 if (!mSocketOut) { 221 return NS_OK; 222 } 223 224 while (!mOutputQueue.empty()) { 225 const OutputData& data = mOutputQueue.front(); 226 227 char* buffer = reinterpret_cast<char*>( 228 const_cast<uint8_t*>(data.GetData().Elements())) + 229 mWriteOffset; 230 uint32_t toWrite = data.GetData().Length() - mWriteOffset; 231 232 uint32_t wrote = 0; 233 nsresult rv = mSocketOut->Write(buffer, toWrite, &wrote); 234 LOG(("WebSocketConnection::OnOutputStreamReady: write %u rv %" PRIx32, 235 wrote, static_cast<uint32_t>(rv))); 236 237 if (rv == NS_BASE_STREAM_WOULD_BLOCK) { 238 mSocketOut->AsyncWait(this, 0, 0, mSocketThread); 239 return rv; 240 } 241 242 if (NS_FAILED(rv)) { 243 LOG(("WebSocketConnection::OnOutputStreamReady %p failed %u\n", this, 244 static_cast<uint32_t>(rv))); 245 mListener->OnError(rv); 246 return NS_OK; 247 } 248 249 mWriteOffset += wrote; 250 251 if (toWrite == wrote) { 252 mWriteOffset = 0; 253 mOutputQueue.pop_front(); 254 } else { 255 mSocketOut->AsyncWait(this, 0, 0, mSocketThread); 256 } 257 } 258 259 return NS_OK; 260 } 261 262 } // namespace mozilla::net