Http3WebTransportSession.cpp (17595B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 // HttpLog.h should generally be included first 7 #include "HttpLog.h" 8 #include "Http3WebTransportSession.h" 9 #include "Http3WebTransportStream.h" 10 #include "Http3Session.h" 11 #include "Http3Stream.h" 12 #include "nsHttpRequestHead.h" 13 #include "nsHttpTransaction.h" 14 #include "nsIClassOfService.h" 15 #include "nsISocketTransport.h" 16 #include "nsSocketTransportService2.h" 17 #include "nsIOService.h" 18 #include "nsHttpHandler.h" 19 20 namespace mozilla::net { 21 22 //----------------------------------------------------------------------------- 23 // Http3TunnelStreamBase 24 //----------------------------------------------------------------------------- 25 26 Http3TunnelStreamBase::Http3TunnelStreamBase(nsAHttpTransaction* trans, 27 Http3SessionBase* aHttp3Session) 28 : Http3StreamBase(trans, aHttp3Session) {} 29 30 bool Http3TunnelStreamBase::ConsumeHeaders(const char* buf, uint32_t avail, 31 uint32_t* countUsed) { 32 LOG3(("Http3TunnelStreamBase::ConsumeHeaders %p avail=%u.", this, avail)); 33 34 mFlatHttpRequestHeaders.Append(buf, avail); 35 // We can use the simple double crlf because firefox is the 36 // only client we are parsing 37 int32_t endHeader = mFlatHttpRequestHeaders.Find("\r\n\r\n"); 38 39 if (endHeader == kNotFound) { 40 // We don't have all the headers yet 41 LOG3( 42 ("Http3TunnelStreamBase::ConsumeHeaders %p " 43 "Need more header bytes. Len = %zu", 44 this, mFlatHttpRequestHeaders.Length())); 45 *countUsed = avail; 46 return false; 47 } 48 49 uint32_t oldLen = mFlatHttpRequestHeaders.Length(); 50 mFlatHttpRequestHeaders.SetLength(endHeader + 2); 51 *countUsed = avail - (oldLen - endHeader) + 4; 52 53 return true; 54 } 55 56 nsresult Http3TunnelStreamBase::TryActivating() { 57 LOG(("Http3TunnelStreamBase::TryActivating [this=%p]", this)); 58 nsHttpRequestHead* head = mTransaction->RequestHead(); 59 60 nsAutoCString host; 61 nsresult rv = head->GetHeader(nsHttp::Host, host); 62 if (NS_FAILED(rv)) { 63 MOZ_ASSERT(false); 64 return rv; 65 } 66 nsAutoCString path; 67 head->Path(path); 68 69 return mSession->TryActivating(""_ns, ""_ns, host, path, 70 mFlatHttpRequestHeaders, &mStreamId, this); 71 } 72 73 nsresult Http3TunnelStreamBase::ReadSegments() { 74 LOG(("Http3TunnelStreamBase::ReadSegments %p mSendState=%d mRecvState=%d", 75 this, mSendState, mRecvState)); 76 if (mSendState == PROCESSING_DATAGRAM) { 77 return OnProcessDatagram(); 78 } 79 80 if ((mRecvState == RECV_DONE) || (mRecvState == ACTIVE) || 81 (mRecvState == CLOSE_PENDING)) { 82 // Don't transmit any request frames if the peer cannot respond or respone 83 // is already done. 84 LOG3( 85 ("Http3TunnelStreamBase %p ReadSegments request stream aborted due to" 86 " response side closure\n", 87 this)); 88 return NS_ERROR_ABORT; 89 } 90 91 nsresult rv = NS_OK; 92 uint32_t transactionBytes; 93 bool again = true; 94 do { 95 transactionBytes = 0; 96 rv = mSocketOutCondition = NS_OK; 97 LOG(("Http3TunnelStreamBase::ReadSegments state=%d [this=%p]", mSendState, 98 this)); 99 switch (mSendState) { 100 case PREPARING_HEADERS: { 101 rv = mTransaction->ReadSegmentsAgain( 102 this, nsIOService::gDefaultSegmentSize, &transactionBytes, &again); 103 } break; 104 case WAITING_TO_ACTIVATE: { 105 // A transaction that had already generated its headers before it was 106 // queued at the session level (due to concurrency concerns) may not 107 // call onReadSegment off the ReadSegments() stack above. 108 LOG3( 109 ("Http3TunnelStreamBase %p ReadSegments forcing OnReadSegment " 110 "call\n", 111 this)); 112 uint32_t wasted = 0; 113 nsresult rv2 = OnReadSegment("", 0, &wasted); 114 LOG3((" OnReadSegment returned 0x%08" PRIx32, 115 static_cast<uint32_t>(rv2))); 116 } break; 117 default: 118 transactionBytes = 0; 119 rv = NS_OK; 120 break; 121 } 122 123 LOG(("Http3TunnelStreamBase::ReadSegments rv=0x%" PRIx32 124 " read=%u sock-cond=%" PRIx32 " again=%d [this=%p]", 125 static_cast<uint32_t>(rv), transactionBytes, 126 static_cast<uint32_t>(mSocketOutCondition), again, this)); 127 128 // XXX some streams return NS_BASE_STREAM_CLOSED to indicate EOF. 129 if (rv == NS_BASE_STREAM_CLOSED && !mTransaction->IsDone()) { 130 rv = NS_OK; 131 transactionBytes = 0; 132 } 133 134 if (NS_FAILED(rv)) { 135 // if the transaction didn't want to write any more data, then 136 // wait for the transaction to call ResumeSend. 137 if (rv == NS_BASE_STREAM_WOULD_BLOCK) { 138 rv = NS_OK; 139 } 140 again = false; 141 } else if (NS_FAILED(mSocketOutCondition)) { 142 if (mSocketOutCondition != NS_BASE_STREAM_WOULD_BLOCK) { 143 rv = mSocketOutCondition; 144 } 145 again = false; 146 } else if (!transactionBytes) { 147 mTransaction->OnTransportStatus(nullptr, NS_NET_STATUS_WAITING_FOR, 0); 148 149 rv = NS_OK; 150 again = false; 151 } 152 // write more to the socket until error or end-of-request... 153 } while (again && gHttpHandler->Active()); 154 return rv; 155 } 156 157 nsresult Http3TunnelStreamBase::WriteSegments() { 158 LOG(("Http3TunnelStreamBase::WriteSegments [this=%p]", this)); 159 nsresult rv = NS_OK; 160 uint32_t countWrittenSingle = 0; 161 bool again = true; 162 163 if (mRecvState == CLOSE_PENDING) { 164 OnClosePending(); 165 mRecvState = RECV_DONE; 166 // This will closed the steam because the stream is Done(). 167 return NS_OK; 168 } 169 170 do { 171 mSocketInCondition = NS_OK; 172 countWrittenSingle = 0; 173 rv = mTransaction->WriteSegmentsAgain( 174 this, nsIOService::gDefaultSegmentSize, &countWrittenSingle, &again); 175 LOG(("Http3TunnelStreamBase::WriteSegments rv=0x%" PRIx32 176 " countWrittenSingle=%" PRIu32 " socketin=%" PRIx32 " [this=%p]", 177 static_cast<uint32_t>(rv), countWrittenSingle, 178 static_cast<uint32_t>(mSocketInCondition), this)); 179 if (mTransaction->IsDone()) { 180 // An HTTP transaction used for setting up a WebTransport session will 181 // receive only response headers and afterward, it will be marked as 182 // done. At this point, the session negotiation has finished and the 183 // WebTransport session transfers into the ACTIVE state. 184 mRecvState = ACTIVE; 185 } 186 187 if (NS_FAILED(rv)) { 188 // if the transaction didn't want to take any more data, then 189 // wait for the transaction to call ResumeRecv. 190 if (rv == NS_BASE_STREAM_WOULD_BLOCK) { 191 rv = NS_OK; 192 } 193 again = false; 194 } else if (NS_FAILED(mSocketInCondition)) { 195 if (mSocketInCondition != NS_BASE_STREAM_WOULD_BLOCK) { 196 rv = mSocketInCondition; 197 } 198 again = false; 199 } else if (mRecvState == ACTIVE) { 200 // again = false; 201 again = OnActivated(); 202 } 203 // read more from the socket until error... 204 } while (again && gHttpHandler->Active()); 205 206 return rv; 207 } 208 209 void Http3TunnelStreamBase::SetResponseHeaders( 210 nsTArray<uint8_t>& aResponseHeaders, bool fin, bool interim) { 211 MOZ_ASSERT(mRecvState == BEFORE_HEADERS || 212 mRecvState == READING_INTERIM_HEADERS); 213 mFlatResponseHeaders.AppendElements(aResponseHeaders); 214 mRecvState = (interim) ? READING_INTERIM_HEADERS : READING_HEADERS; 215 } 216 217 nsresult Http3TunnelStreamBase::OnWriteSegment(char* buf, uint32_t count, 218 uint32_t* countWritten) { 219 LOG(("Http3TunnelStreamBase::OnWriteSegment [this=%p, state=%d", this, 220 mRecvState)); 221 nsresult rv = NS_OK; 222 switch (mRecvState) { 223 case BEFORE_HEADERS: { 224 *countWritten = 0; 225 rv = NS_BASE_STREAM_WOULD_BLOCK; 226 } break; 227 case READING_HEADERS: 228 case READING_INTERIM_HEADERS: { 229 // SetResponseHeaders should have been previously called. 230 MOZ_ASSERT(!mFlatResponseHeaders.IsEmpty(), "Headers empty!"); 231 *countWritten = (mFlatResponseHeaders.Length() > count) 232 ? count 233 : mFlatResponseHeaders.Length(); 234 memcpy(buf, mFlatResponseHeaders.Elements(), *countWritten); 235 236 mFlatResponseHeaders.RemoveElementsAt(0, *countWritten); 237 if (mFlatResponseHeaders.Length() == 0) { 238 if (mRecvState == READING_INTERIM_HEADERS) { 239 // neqo makes sure that fin cannot be received before the final 240 // headers are received. 241 mRecvState = BEFORE_HEADERS; 242 } else { 243 mRecvState = ACTIVE; 244 } 245 } 246 247 if (*countWritten == 0) { 248 rv = NS_BASE_STREAM_WOULD_BLOCK; 249 } else { 250 mTransaction->OnTransportStatus(nullptr, NS_NET_STATUS_RECEIVING_FROM, 251 0); 252 } 253 } break; 254 case ACTIVE: 255 case CLOSE_PENDING: 256 case RECV_DONE: 257 rv = NS_ERROR_UNEXPECTED; 258 } 259 260 // Remember the error received from lower layers. A stream pipe may overwrite 261 // it. 262 // If rv == NS_OK this will reset mSocketInCondition. 263 mSocketInCondition = rv; 264 265 return rv; 266 } 267 268 nsresult Http3TunnelStreamBase::OnReadSegment(const char* buf, uint32_t count, 269 uint32_t* countRead) { 270 LOG(("Http3TunnelStreamBase::OnReadSegment count=%u state=%d [this=%p]", 271 count, mSendState, this)); 272 273 nsresult rv = NS_OK; 274 275 switch (mSendState) { 276 case PREPARING_HEADERS: { 277 if (!ConsumeHeaders(buf, count, countRead)) { 278 break; 279 } 280 mSendState = WAITING_TO_ACTIVATE; 281 } 282 [[fallthrough]]; 283 case WAITING_TO_ACTIVATE: 284 rv = TryActivating(); 285 if (rv == NS_BASE_STREAM_WOULD_BLOCK) { 286 LOG3( 287 ("Http3TunnelStreamBase::OnReadSegment %p cannot activate now. " 288 "queued.\n", 289 this)); 290 break; 291 } 292 if (NS_FAILED(rv)) { 293 LOG3( 294 ("Http3TunnelStreamBase::OnReadSegment %p cannot activate " 295 "error=0x%" PRIx32 ".", 296 this, static_cast<uint32_t>(rv))); 297 break; 298 } 299 300 // Successfully activated. 301 mTransaction->OnTransportStatus(nullptr, NS_NET_STATUS_SENDING_TO, 0); 302 303 mSendState = PROCESSING_DATAGRAM; 304 break; 305 default: 306 MOZ_ASSERT(false, "We are done sending this request!"); 307 rv = NS_ERROR_UNEXPECTED; 308 break; 309 } 310 311 mSocketOutCondition = rv; 312 313 return mSocketOutCondition; 314 } 315 316 void Http3TunnelStreamBase::TransactionIsDone(nsresult aResult) { 317 mTransaction->Close(aResult); 318 mTransaction = nullptr; 319 } 320 321 //----------------------------------------------------------------------------- 322 // Http3WebTransportSession 323 //----------------------------------------------------------------------------- 324 325 Http3WebTransportSession::Http3WebTransportSession(nsAHttpTransaction* trans, 326 Http3Session* aHttp3Session) 327 : Http3TunnelStreamBase(trans, aHttp3Session) { 328 LOG(("Http3WebTransportSession ctor %p", this)); 329 } 330 331 Http3WebTransportSession::~Http3WebTransportSession() = default; 332 333 uint64_t Http3WebTransportSession::GetStreamId() const { 334 return Http3StreamBase::StreamId(); 335 } 336 337 void Http3WebTransportSession::Close(nsresult aResult) { 338 LOG(("Http3WebTransportSession::Close %p", this)); 339 if (mListener) { 340 mListener->OnSessionClosed(NS_SUCCEEDED(aResult), 0, ""_ns); 341 mListener = nullptr; 342 } 343 if (mTransaction) { 344 mTransaction->Close(aResult); 345 mTransaction = nullptr; 346 } 347 mRecvState = RECV_DONE; 348 mSendState = SEND_DONE; 349 350 if (mSession) { 351 mSession->CloseWebTransportConn(); 352 mSession = nullptr; 353 } 354 } 355 356 void Http3WebTransportSession::OnClosePending() { 357 mSession->CloseWebTransport(mStreamId, mStatus, mReason); 358 } 359 360 void Http3WebTransportSession::OnSessionClosed(bool aCleanly, uint32_t aStatus, 361 const nsACString& aReason) { 362 if (mTransaction) { 363 mTransaction->Close(NS_BASE_STREAM_CLOSED); 364 mTransaction = nullptr; 365 } 366 if (mListener) { 367 mListener->OnSessionClosed(aCleanly, aStatus, aReason); 368 mListener = nullptr; 369 } 370 mRecvState = RECV_DONE; 371 mSendState = SEND_DONE; 372 373 mSession->CloseWebTransportConn(); 374 } 375 376 void Http3WebTransportSession::CloseSession(uint32_t aStatus, 377 const nsACString& aReason) { 378 if ((mRecvState != CLOSE_PENDING) && (mRecvState != RECV_DONE)) { 379 mStatus = aStatus; 380 mReason = aReason; 381 mSession->ConnectSlowConsumer(this); 382 mRecvState = CLOSE_PENDING; 383 mSendState = SEND_DONE; 384 } 385 mListener = nullptr; 386 } 387 388 void Http3WebTransportSession::CreateOutgoingBidirectionalStream( 389 std::function<void(Result<RefPtr<WebTransportStreamBase>, nsresult>&&)>&& 390 aCallback) { 391 return CreateStreamInternal(true, std::move(aCallback)); 392 } 393 394 void Http3WebTransportSession::CreateOutgoingUnidirectionalStream( 395 std::function<void(Result<RefPtr<WebTransportStreamBase>, nsresult>&&)>&& 396 aCallback) { 397 return CreateStreamInternal(false, std::move(aCallback)); 398 } 399 400 void Http3WebTransportSession::CreateStreamInternal( 401 bool aBidi, 402 std::function<void(Result<RefPtr<WebTransportStreamBase>, nsresult>&&)>&& 403 aCallback) { 404 LOG(("Http3WebTransportSession::CreateStreamInternal this=%p aBidi=%d", this, 405 aBidi)); 406 if (mRecvState != ACTIVE) { 407 aCallback(Err(NS_ERROR_NOT_AVAILABLE)); 408 return; 409 } 410 411 RefPtr<Http3WebTransportStream> stream = 412 aBidi ? new Http3WebTransportStream(mSession, mStreamId, 413 WebTransportStreamType::BiDi, 414 std::move(aCallback)) 415 : new Http3WebTransportStream(mSession, mStreamId, 416 WebTransportStreamType::UniDi, 417 std::move(aCallback)); 418 mSession->StreamHasDataToWrite(stream); 419 // Put the newly created stream in to |mStreams| to keep it alive. 420 mStreams.AppendElement(std::move(stream)); 421 } 422 423 // This is called by Http3Session::TryActivatingWebTransportStream. When called, 424 // this means a WebTransport stream is successfully activated and the stream 425 // will be managed by Http3Session. 426 void Http3WebTransportSession::RemoveWebTransportStream( 427 Http3WebTransportStream* aStream) { 428 LOG( 429 ("Http3WebTransportSession::RemoveWebTransportStream " 430 "this=%p aStream=%p", 431 this, aStream)); 432 DebugOnly<bool> existed = mStreams.RemoveElement(aStream); 433 MOZ_ASSERT(existed); 434 } 435 436 already_AddRefed<Http3WebTransportStream> 437 Http3WebTransportSession::OnIncomingWebTransportStream( 438 WebTransportStreamType aType, uint64_t aId) { 439 LOG( 440 ("Http3WebTransportSession::OnIncomingWebTransportStream " 441 "this=%p", 442 this)); 443 444 if (mRecvState != ACTIVE) { 445 return nullptr; 446 } 447 448 MOZ_ASSERT(!mTransaction); 449 RefPtr<Http3WebTransportStream> stream = 450 new Http3WebTransportStream(mSession, mStreamId, aType, aId); 451 if (NS_FAILED(stream->InitInputPipe())) { 452 return nullptr; 453 } 454 455 if (aType == WebTransportStreamType::BiDi) { 456 if (NS_FAILED(stream->InitOutputPipe())) { 457 return nullptr; 458 } 459 } 460 461 if (!mListener) { 462 return nullptr; 463 } 464 465 if (nsCOMPtr<WebTransportSessionEventListenerInternal> listener = 466 do_QueryInterface(mListener)) { 467 listener->OnIncomingStreamAvailableInternal(stream); 468 } 469 return stream.forget(); 470 } 471 472 void Http3WebTransportSession::SendDatagram(nsTArray<uint8_t>&& aData, 473 uint64_t aTrackingId) { 474 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 475 LOG(("Http3WebTransportSession::SendDatagram this=%p", this)); 476 if (mSendState != PROCESSING_DATAGRAM) { 477 return; 478 } 479 480 mSession->SendDatagram(this, aData, aTrackingId); 481 mSession->StreamHasDataToWrite(this); 482 } 483 484 void Http3WebTransportSession::OnDatagramReceived(nsTArray<uint8_t>&& aData) { 485 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 486 LOG(("Http3WebTransportSession::OnDatagramReceived this=%p", this)); 487 if (mRecvState != ACTIVE || !mListener) { 488 return; 489 } 490 491 if (nsCOMPtr<WebTransportSessionEventListenerInternal> listener = 492 do_QueryInterface(mListener)) { 493 listener->OnDatagramReceivedInternal(std::move(aData)); 494 } 495 } 496 497 void Http3WebTransportSession::GetMaxDatagramSize() { 498 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 499 if (mRecvState != ACTIVE || !mListener) { 500 return; 501 } 502 503 uint64_t size = mSession->MaxDatagramSize(mStreamId); 504 mListener->OnMaxDatagramSize(size); 505 } 506 507 void Http3WebTransportSession::OnOutgoingDatagramOutCome( 508 uint64_t aId, WebTransportSessionEventListener::DatagramOutcome aOutCome) { 509 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 510 LOG(("Http3WebTransportSession::OnOutgoingDatagramOutCome this=%p id=%" PRIx64 511 ", outCome=%d mRecvState=%d", 512 this, aId, static_cast<uint32_t>(aOutCome), mRecvState)); 513 if (mRecvState != ACTIVE || !mListener || !aId) { 514 return; 515 } 516 517 mListener->OnOutgoingDatagramOutCome(aId, aOutCome); 518 } 519 520 void Http3WebTransportSession::OnStreamStopSending(uint64_t aId, 521 nsresult aError) { 522 LOG(("OnStreamStopSending id:%" PRId64, aId)); 523 if (!mListener) { 524 return; 525 } 526 527 mListener->OnStopSending(aId, aError); 528 } 529 530 void Http3WebTransportSession::OnStreamReset(uint64_t aId, nsresult aError) { 531 LOG(("OnStreamReset id:%" PRId64, aId)); 532 if (!mListener) { 533 return; 534 } 535 536 mListener->OnResetReceived(aId, aError); 537 } 538 539 } // namespace mozilla::net