Http2Stream.cpp (5096B)
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 10 #include "Http2Stream.h" 11 #include "nsHttp.h" 12 #include "nsHttpConnectionInfo.h" 13 #include "nsHttpRequestHead.h" 14 #include "nsISocketTransport.h" 15 #include "Http2Session.h" 16 #include "nsIRequestContext.h" 17 #include "nsHttpTransaction.h" 18 #include "nsSocketTransportService2.h" 19 #include "mozilla/glean/NetwerkProtocolHttpMetrics.h" 20 21 namespace mozilla::net { 22 23 Http2Stream::Http2Stream(nsAHttpTransaction* httpTransaction, 24 Http2Session* session, int32_t priority, uint64_t bcId) 25 : Http2StreamBase((httpTransaction->QueryHttpTransaction()) 26 ? httpTransaction->QueryHttpTransaction()->BrowserId() 27 : 0, 28 session, priority, bcId), 29 mTransaction(httpTransaction) { 30 LOG1(("Http2Stream::Http2Stream %p trans=%p", this, httpTransaction)); 31 } 32 33 Http2Stream::~Http2Stream() {} 34 35 void Http2Stream::CloseStream(nsresult reason) { 36 if (reason == NS_ERROR_NET_RESET && 37 mTransaction->ConnectionInfo()->IsHttp3ProxyConnection()) { 38 // If we got NS_ERROR_NET_RESET, the transaction will be retried. When 39 // MASQUE proxy is used, keep the Alt-Svc in the connection info. Dropping 40 // it could trigger an unintended proxy connection fallback. 41 mTransaction->DoNotRemoveAltSvc(); 42 } 43 mTransaction->Close(reason); 44 mSession = nullptr; 45 mClosed = true; 46 } 47 48 uint32_t Http2Stream::GetWireStreamId() { 49 // >0 even numbered IDs are pushed streams. 50 // odd numbered IDs are pulled streams. 51 // 0 is the sink for a pushed stream. 52 if (!mStreamID) { 53 return 0; 54 } 55 56 if (mState == RESERVED_BY_REMOTE) { 57 // h2-14 prevents sending a window update in this state 58 return 0; 59 } 60 return mStreamID; 61 } 62 63 nsresult Http2Stream::OnWriteSegment(char* buf, uint32_t count, 64 uint32_t* countWritten) { 65 LOG3(("Http2Stream::OnWriteSegment %p count=%d state=%x 0x%X\n", this, count, 66 mUpstreamState, mStreamID)); 67 68 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 69 MOZ_ASSERT(mSegmentWriter); 70 71 return Http2StreamBase::OnWriteSegment(buf, count, countWritten); 72 } 73 74 nsresult Http2Stream::CallToReadData(uint32_t count, uint32_t* countRead) { 75 return mTransaction->ReadSegments(this, count, countRead); 76 } 77 78 nsresult Http2Stream::CallToWriteData(uint32_t count, uint32_t* countWritten) { 79 return mTransaction->WriteSegments(this, count, countWritten); 80 } 81 82 // This is really a headers frame, but open is pretty clear from a workflow pov 83 nsresult Http2Stream::GenerateHeaders(nsCString& aCompressedData, 84 uint8_t& firstFrameFlags) { 85 nsHttpRequestHead* head = mTransaction->RequestHead(); 86 nsAutoCString requestURI; 87 head->RequestURI(requestURI); 88 RefPtr<Http2Session> session = Session(); 89 LOG3(("Http2Stream %p Stream ID 0x%X [session=%p] for URI %s\n", this, 90 mStreamID, session.get(), requestURI.get())); 91 92 nsAutoCString authorityHeader; 93 nsresult rv = head->GetHeader(nsHttp::Host, authorityHeader); 94 if (NS_FAILED(rv)) { 95 MOZ_ASSERT(false); 96 return rv; 97 } 98 99 nsDependentCString scheme(head->IsHTTPS() ? "https" : "http"); 100 101 nsAutoCString method; 102 nsAutoCString path; 103 head->Method(method); 104 head->Path(path); 105 106 bool mayAddTEHeader = true; 107 nsAutoCString teHeader; 108 rv = head->GetHeader(nsHttp::TE, teHeader); 109 if (NS_SUCCEEDED(rv) && teHeader.Equals("moz_no_te_trailers"_ns)) { 110 // This is a hack that allows extensions or devtools to 111 // skip adding the TE header. This is necessary to be able to 112 // ship interventions when websites misbehave when the TE:trailers 113 // header is sent (see bug 1954533). 114 mayAddTEHeader = false; 115 } 116 117 rv = session->Compressor()->EncodeHeaderBlock( 118 mFlatHttpRequestHeaders, method, path, authorityHeader, scheme, 119 EmptyCString(), false, aCompressedData, mayAddTEHeader); 120 NS_ENSURE_SUCCESS(rv, rv); 121 122 int64_t clVal = session->Compressor()->GetParsedContentLength(); 123 if (clVal != -1) { 124 mRequestBodyLenRemaining = clVal; 125 } 126 127 // Determine whether to put the fin bit on the header frame or whether 128 // to wait for a data packet to put it on. 129 130 if (head->IsGet() || head->IsHead()) { 131 // for GET and HEAD place the fin bit right on the 132 // header packet 133 firstFrameFlags |= Http2Session::kFlag_END_STREAM; 134 } else if (head->IsPost() || head->IsPut() || head->IsConnect()) { 135 // place fin in a data frame even for 0 length messages for iterop 136 } else if (!mRequestBodyLenRemaining) { 137 // for other HTTP extension methods, rely on the content-length 138 // to determine whether or not to put fin on headers 139 firstFrameFlags |= Http2Session::kFlag_END_STREAM; 140 } 141 142 return NS_OK; 143 } 144 145 } // namespace mozilla::net