Http3Stream.cpp (16086B)
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 // HttpLog.h should generally be included first 8 #include "HttpLog.h" 9 #include "Http3Session.h" 10 #include "Http3Stream.h" 11 #include "nsHttpRequestHead.h" 12 #include "nsHttpTransaction.h" 13 #include "nsIClassOfService.h" 14 #include "nsISocketTransport.h" 15 #include "nsSocketTransportService2.h" 16 #include "mozilla/StaticPrefs_network.h" 17 #include "mozilla/glean/NetwerkProtocolHttpMetrics.h" 18 #include "nsIOService.h" 19 #include "nsHttpHandler.h" 20 21 #include <stdio.h> 22 23 namespace mozilla { 24 namespace net { 25 26 Http3StreamBase::Http3StreamBase(nsAHttpTransaction* trans, 27 Http3SessionBase* session) 28 : mTransaction(trans), mSession(session) {} 29 30 Http3StreamBase::~Http3StreamBase() = default; 31 32 Http3Stream::Http3Stream(nsAHttpTransaction* httpTransaction, 33 Http3Session* session, const ClassOfService& cos, 34 uint64_t currentBrowserId) 35 : Http3StreamBase(httpTransaction, session), 36 mCurrentBrowserId(currentBrowserId) { 37 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 38 LOG3(("Http3Stream::Http3Stream [this=%p]", this)); 39 40 nsHttpTransaction* trans = mTransaction->QueryHttpTransaction(); 41 int32_t priority = nsISupportsPriority::PRIORITY_NORMAL; 42 if (trans) { 43 mTransactionBrowserId = trans->BrowserId(); 44 priority = trans->Priority(); 45 } 46 47 mPriorityUrgency = nsHttpHandler::UrgencyFromCoSFlags(cos.Flags(), priority); 48 SetIncremental(cos.Incremental()); 49 } 50 51 void Http3Stream::Close(nsresult aResult) { 52 mRecvState = RECV_DONE; 53 mTransaction->Close(aResult); 54 // Clear the mSession to break the cycle. 55 mSession = nullptr; 56 } 57 58 bool Http3Stream::GetHeadersString(const char* buf, uint32_t avail, 59 uint32_t* countUsed) { 60 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 61 LOG3(("Http3Stream::GetHeadersString %p avail=%u.", this, avail)); 62 63 mFlatHttpRequestHeaders.Append(buf, avail); 64 // We can use the simple double crlf because firefox is the 65 // only client we are parsing 66 int32_t endHeader = mFlatHttpRequestHeaders.Find("\r\n\r\n"); 67 68 if (endHeader == kNotFound) { 69 // We don't have all the headers yet 70 LOG3( 71 ("Http3Stream::GetHeadersString %p " 72 "Need more header bytes. Len = %zu", 73 this, mFlatHttpRequestHeaders.Length())); 74 *countUsed = avail; 75 return false; 76 } 77 78 uint32_t oldLen = mFlatHttpRequestHeaders.Length(); 79 mFlatHttpRequestHeaders.SetLength(endHeader + 2); 80 *countUsed = avail - (oldLen - endHeader) + 4; 81 82 return true; 83 } 84 85 void Http3Stream::SetIncremental(bool incremental) { 86 mPriorityIncremental = incremental; 87 } 88 89 nsresult Http3Stream::TryActivating() { 90 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 91 LOG(("Http3Stream::TryActivating [this=%p]", this)); 92 nsHttpRequestHead* head = mTransaction->RequestHead(); 93 94 nsAutoCString authorityHeader; 95 nsresult rv = head->GetHeader(nsHttp::Host, authorityHeader); 96 if (NS_FAILED(rv)) { 97 MOZ_ASSERT(false); 98 return rv; 99 } 100 101 nsDependentCString scheme(head->IsHTTPS() ? "https" : "http"); 102 103 nsAutoCString method; 104 nsAutoCString path; 105 head->Method(method); 106 head->Path(path); 107 108 #ifdef DEBUG 109 nsAutoCString contentLength; 110 if (NS_SUCCEEDED(head->GetHeader(nsHttp::Content_Length, contentLength))) { 111 int64_t len; 112 if (nsHttp::ParseInt64(contentLength.get(), nullptr, &len)) { 113 mRequestBodyLenExpected = len; 114 } 115 } 116 #endif 117 118 return mSession->TryActivating(method, scheme, authorityHeader, path, 119 mFlatHttpRequestHeaders, &mStreamId, this); 120 } 121 122 void Http3Stream::CurrentBrowserIdChanged(uint64_t id) { 123 MOZ_ASSERT(StaticPrefs::network_http_active_tab_priority()); 124 125 bool previouslyFocused = (mCurrentBrowserId == mTransactionBrowserId); 126 mCurrentBrowserId = id; 127 bool nowFocused = (mCurrentBrowserId == mTransactionBrowserId); 128 129 if (!StaticPrefs:: 130 network_http_http3_send_background_tabs_deprioritization() || 131 previouslyFocused == nowFocused) { 132 return; 133 } 134 135 mSession->SendPriorityUpdateFrame(mStreamId, PriorityUrgency(), 136 PriorityIncremental()); 137 } 138 139 nsresult Http3Stream::OnReadSegment(const char* buf, uint32_t count, 140 uint32_t* countRead) { 141 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 142 143 LOG(("Http3Stream::OnReadSegment count=%u state=%d [this=%p]", count, 144 mSendState, this)); 145 146 nsresult rv = NS_OK; 147 148 switch (mSendState) { 149 case PREPARING_HEADERS: { 150 bool done = GetHeadersString(buf, count, countRead); 151 152 if (*countRead) { 153 mTotalSent += *countRead; 154 } 155 156 if (!done) { 157 break; 158 } 159 mSendState = WAITING_TO_ACTIVATE; 160 } 161 [[fallthrough]]; 162 case WAITING_TO_ACTIVATE: 163 rv = TryActivating(); 164 if (rv == NS_BASE_STREAM_WOULD_BLOCK) { 165 LOG3(("Http3Stream::OnReadSegment %p cannot activate now. queued.\n", 166 this)); 167 rv = *countRead ? NS_OK : NS_BASE_STREAM_WOULD_BLOCK; 168 break; 169 } 170 if (NS_FAILED(rv)) { 171 LOG3(("Http3Stream::OnReadSegment %p cannot activate error=0x%" PRIx32 172 ".", 173 this, static_cast<uint32_t>(rv))); 174 break; 175 } 176 177 // Successfully activated. 178 mTransaction->OnTransportStatus(nullptr, NS_NET_STATUS_SENDING_TO, 179 mTotalSent); 180 181 mSendState = SENDING_BODY; 182 break; 183 case SENDING_BODY: { 184 rv = mSession->SendRequestBody(mStreamId, buf, count, countRead); 185 if (rv == NS_BASE_STREAM_WOULD_BLOCK) { 186 mSendingBlockedByFlowControlCount++; 187 mBlockedByFlowControl = true; 188 } 189 190 if (NS_FAILED(rv)) { 191 LOG3( 192 ("Http3Stream::OnReadSegment %p sending body returns " 193 "error=0x%" PRIx32 ".", 194 this, static_cast<uint32_t>(rv))); 195 break; 196 } 197 198 #ifdef DEBUG 199 mRequestBodyLenSent += *countRead; 200 #endif 201 mTotalSent += *countRead; 202 mTransaction->OnTransportStatus(nullptr, NS_NET_STATUS_SENDING_TO, 203 mTotalSent); 204 } break; 205 case EARLY_RESPONSE: 206 // We do not need to send the rest of the request, so just ignore it. 207 *countRead = count; 208 #ifdef DEBUG 209 mRequestBodyLenSent += count; 210 #endif 211 break; 212 default: 213 MOZ_ASSERT(false, "We are done sending this request!"); 214 rv = NS_ERROR_UNEXPECTED; 215 break; 216 } 217 218 mSocketOutCondition = rv; 219 220 return mSocketOutCondition; 221 } 222 223 void Http3Stream::SetResponseHeaders(nsTArray<uint8_t>& aResponseHeaders, 224 bool aFin, bool interim) { 225 MOZ_ASSERT(mRecvState == BEFORE_HEADERS || 226 mRecvState == READING_INTERIM_HEADERS); 227 mFlatResponseHeaders.AppendElements(aResponseHeaders); 228 mRecvState = (interim) ? READING_INTERIM_HEADERS : READING_HEADERS; 229 mDataReceived = true; 230 mFin = aFin; 231 } 232 233 void Http3Stream::StopSending() { 234 MOZ_ASSERT((mSendState == SENDING_BODY) || (mSendState == SEND_DONE)); 235 if (mSendState == SENDING_BODY) { 236 mSendState = EARLY_RESPONSE; 237 } 238 } 239 240 nsresult Http3Stream::OnWriteSegment(char* buf, uint32_t count, 241 uint32_t* countWritten) { 242 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 243 244 LOG(("Http3Stream::OnWriteSegment [this=%p, state=%d", this, mRecvState)); 245 nsresult rv = NS_OK; 246 switch (mRecvState) { 247 case BEFORE_HEADERS: { 248 *countWritten = 0; 249 rv = NS_BASE_STREAM_WOULD_BLOCK; 250 } break; 251 case READING_HEADERS: 252 case READING_INTERIM_HEADERS: { 253 // SetResponseHeaders should have been previously called. 254 MOZ_ASSERT(!mFlatResponseHeaders.IsEmpty(), "Headers empty!"); 255 *countWritten = (mFlatResponseHeaders.Length() > count) 256 ? count 257 : mFlatResponseHeaders.Length(); 258 memcpy(buf, mFlatResponseHeaders.Elements(), *countWritten); 259 260 mFlatResponseHeaders.RemoveElementsAt(0, *countWritten); 261 if (mFlatResponseHeaders.Length() == 0) { 262 if (mRecvState == READING_INTERIM_HEADERS) { 263 // neqo makes sure that fin cannot be received before the final 264 // headers are received. 265 MOZ_ASSERT(!mFin); 266 mRecvState = BEFORE_HEADERS; 267 } else { 268 mRecvState = mFin ? RECEIVED_FIN : READING_DATA; 269 } 270 } 271 272 if (*countWritten == 0) { 273 rv = NS_BASE_STREAM_WOULD_BLOCK; 274 } else { 275 mTotalRead += *countWritten; 276 mTransaction->OnTransportStatus(nullptr, NS_NET_STATUS_RECEIVING_FROM, 277 mTotalRead); 278 } 279 } break; 280 case READING_DATA: { 281 rv = mSession->ReadResponseData(mStreamId, buf, count, countWritten, 282 &mFin); 283 if (NS_FAILED(rv)) { 284 break; 285 } 286 if (*countWritten == 0) { 287 if (mFin) { 288 mRecvState = RECV_DONE; 289 rv = NS_BASE_STREAM_CLOSED; 290 } else { 291 rv = NS_BASE_STREAM_WOULD_BLOCK; 292 } 293 } else { 294 mTotalRead += *countWritten; 295 mTransaction->OnTransportStatus(nullptr, NS_NET_STATUS_RECEIVING_FROM, 296 mTotalRead); 297 298 if (mFin) { 299 mRecvState = RECEIVED_FIN; 300 } 301 } 302 } break; 303 case RECEIVED_FIN: 304 rv = NS_BASE_STREAM_CLOSED; 305 mRecvState = RECV_DONE; 306 break; 307 case RECV_DONE: 308 rv = NS_ERROR_UNEXPECTED; 309 } 310 311 // Remember the error received from lower layers. A stream pipe may overwrite 312 // it. 313 // If rv == NS_OK this will reset mSocketInCondition. 314 mSocketInCondition = rv; 315 316 return rv; 317 } 318 319 nsresult Http3Stream::ReadSegments() { 320 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 321 322 if (mRecvState == RECV_DONE) { 323 // Don't transmit any request frames if the peer cannot respond or respone 324 // is already done. 325 LOG3( 326 ("Http3Stream %p ReadSegments request stream aborted due to" 327 " response side closure\n", 328 this)); 329 return NS_ERROR_ABORT; 330 } 331 332 nsresult rv = NS_OK; 333 uint32_t transactionBytes; 334 bool again = true; 335 do { 336 transactionBytes = 0; 337 rv = mSocketOutCondition = NS_OK; 338 LOG(("Http3Stream::ReadSegments state=%d [this=%p]", mSendState, this)); 339 switch (mSendState) { 340 case WAITING_TO_ACTIVATE: { 341 // A transaction that had already generated its headers before it was 342 // queued at the session level (due to concurrency concerns) may not 343 // call onReadSegment off the ReadSegments() stack above. 344 LOG3( 345 ("Http3Stream %p ReadSegments forcing OnReadSegment call\n", this)); 346 uint32_t wasted = 0; 347 nsresult rv2 = OnReadSegment("", 0, &wasted); 348 LOG3((" OnReadSegment returned 0x%08" PRIx32, 349 static_cast<uint32_t>(rv2))); 350 if (mSendState != SENDING_BODY) { 351 break; 352 } 353 } 354 // If we are in state SENDING_BODY we can continue sending data. 355 [[fallthrough]]; 356 case PREPARING_HEADERS: 357 case SENDING_BODY: { 358 rv = mTransaction->ReadSegmentsAgain( 359 this, nsIOService::gDefaultSegmentSize, &transactionBytes, &again); 360 } break; 361 default: 362 transactionBytes = 0; 363 rv = NS_OK; 364 break; 365 } 366 367 LOG(("Http3Stream::ReadSegments rv=0x%" PRIx32 " read=%u sock-cond=%" PRIx32 368 " again=%d [this=%p]", 369 static_cast<uint32_t>(rv), transactionBytes, 370 static_cast<uint32_t>(mSocketOutCondition), again, this)); 371 372 // XXX some streams return NS_BASE_STREAM_CLOSED to indicate EOF. 373 if (rv == NS_BASE_STREAM_CLOSED && !mTransaction->IsDone()) { 374 rv = NS_OK; 375 transactionBytes = 0; 376 } 377 378 if (NS_FAILED(rv)) { 379 // if the transaction didn't want to write any more data, then 380 // wait for the transaction to call ResumeSend. 381 if (rv == NS_BASE_STREAM_WOULD_BLOCK) { 382 rv = NS_OK; 383 } 384 again = false; 385 } else if (NS_FAILED(mSocketOutCondition)) { 386 if (mSocketOutCondition != NS_BASE_STREAM_WOULD_BLOCK) { 387 rv = mSocketOutCondition; 388 } 389 again = false; 390 } else if (!transactionBytes) { 391 mTransaction->OnTransportStatus(nullptr, NS_NET_STATUS_WAITING_FOR, 0); 392 mSession->CloseSendingSide(mStreamId); 393 mSendState = SEND_DONE; 394 glean::http3::sending_blocked_by_flow_control_per_trans 395 .AccumulateSingleSample(mSendingBlockedByFlowControlCount); 396 397 #ifdef DEBUG 398 MOZ_ASSERT(mRequestBodyLenSent == mRequestBodyLenExpected); 399 #endif 400 rv = NS_OK; 401 again = false; 402 } 403 // write more to the socket until error or end-of-request... 404 } while (again && gHttpHandler->Active()); 405 return rv; 406 } 407 408 nsresult Http3Stream::WriteSegments() { 409 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 410 LOG(("Http3Stream::WriteSegments [this=%p]", this)); 411 nsresult rv = NS_OK; 412 uint32_t countWrittenSingle = 0; 413 bool again = true; 414 415 do { 416 mSocketInCondition = NS_OK; 417 countWrittenSingle = 0; 418 rv = mTransaction->WriteSegmentsAgain( 419 this, nsIOService::gDefaultSegmentSize, &countWrittenSingle, &again); 420 LOG(("Http3Stream::WriteSegments rv=0x%" PRIx32 421 " countWrittenSingle=%" PRIu32 " socketin=%" PRIx32 " [this=%p]", 422 static_cast<uint32_t>(rv), countWrittenSingle, 423 static_cast<uint32_t>(mSocketInCondition), this)); 424 if (mTransaction->IsDone()) { 425 // If a transaction has read the amount of data specified in 426 // Content-Length it is marked as done.The Http3Stream should be 427 // marked as done as well to start the process of cleanup and 428 // closure. 429 mRecvState = RECV_DONE; 430 } 431 432 if (NS_FAILED(rv)) { 433 // if the transaction didn't want to take any more data, then 434 // wait for the transaction to call ResumeRecv. 435 if (rv == NS_BASE_STREAM_WOULD_BLOCK) { 436 rv = NS_OK; 437 } 438 again = false; 439 } else if (NS_FAILED(mSocketInCondition)) { 440 if (mSocketInCondition != NS_BASE_STREAM_WOULD_BLOCK) { 441 rv = mSocketInCondition; 442 } 443 again = false; 444 } 445 // read more from the socket until error... 446 } while (again && gHttpHandler->Active()); 447 448 return rv; 449 } 450 451 bool Http3Stream::Do0RTT() { 452 MOZ_ASSERT(mTransaction); 453 mAttempting0RTT = mTransaction->Do0RTT(); 454 return mAttempting0RTT; 455 } 456 457 nsresult Http3Stream::Finish0RTT(bool aRestart) { 458 MOZ_ASSERT(mTransaction); 459 mAttempting0RTT = false; 460 nsresult rv = mTransaction->Finish0RTT(aRestart, false); 461 if (aRestart) { 462 nsHttpTransaction* trans = mTransaction->QueryHttpTransaction(); 463 if (trans) { 464 trans->Refused0RTT(); 465 } 466 467 // Reset Http3Sream states as well. 468 mSendState = PREPARING_HEADERS; 469 mRecvState = BEFORE_HEADERS; 470 mStreamId = UINT64_MAX; 471 mFlatHttpRequestHeaders = ""; 472 mQueued = false; 473 mDataReceived = false; 474 mResetRecv = false; 475 mFlatResponseHeaders.TruncateLength(0); 476 mTotalSent = 0; 477 mTotalRead = 0; 478 mFin = false; 479 mSendingBlockedByFlowControlCount = 0; 480 mSocketInCondition = NS_ERROR_NOT_INITIALIZED; 481 mSocketOutCondition = NS_ERROR_NOT_INITIALIZED; 482 } 483 484 return rv; 485 } 486 487 uint8_t Http3Stream::PriorityUrgency() { 488 if (!StaticPrefs::network_http_http3_priority()) { 489 // send default priority which is equivalent to sending no priority 490 return 3; 491 } 492 493 if (StaticPrefs::network_http_http3_send_background_tabs_deprioritization() && 494 mCurrentBrowserId != mTransactionBrowserId) { 495 // Low priority 496 return 6; 497 } 498 return mPriorityUrgency; 499 } 500 501 bool Http3Stream::PriorityIncremental() { 502 if (!StaticPrefs::network_http_http3_priority()) { 503 // send default priority which is equivalent to sending no priority 504 return false; 505 } 506 return mPriorityIncremental; 507 } 508 509 } // namespace net 510 } // namespace mozilla