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 #ifndef mozilla_net_Http2Stream_h 7 #define mozilla_net_Http2Stream_h 8 9 // HTTP/2 - RFC7540 10 // https://www.rfc-editor.org/rfc/rfc7540.txt 11 12 #include "mozilla/Attributes.h" 13 #include "mozilla/UniquePtr.h" 14 #include "nsAHttpTransaction.h" 15 #include "nsISupportsPriority.h" 16 #include "SimpleBuffer.h" 17 #include "nsISupportsImpl.h" 18 #include "nsIURI.h" 19 20 class nsIInputStream; 21 class nsIOutputStream; 22 23 namespace mozilla { 24 class OriginAttributes; 25 } 26 27 namespace mozilla { 28 namespace net { 29 30 class nsStandardURL; 31 class Http2Session; 32 class Http2Decompressor; 33 34 class Http2Stream : public nsAHttpSegmentReader, 35 public nsAHttpSegmentWriter, 36 public SupportsWeakPtr { 37 public: 38 NS_DECL_NSAHTTPSEGMENTREADER 39 NS_DECL_NSAHTTPSEGMENTWRITER 40 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Http2Stream, override) 41 42 enum stateType { 43 IDLE, 44 RESERVED_BY_REMOTE, 45 OPEN, 46 CLOSED_BY_LOCAL, 47 CLOSED_BY_REMOTE, 48 CLOSED 49 }; 50 51 const static int32_t kNormalPriority = 0x1000; 52 const static int32_t kWorstPriority = 53 kNormalPriority + nsISupportsPriority::PRIORITY_LOWEST; 54 const static int32_t kBestPriority = 55 kNormalPriority + nsISupportsPriority::PRIORITY_HIGHEST; 56 57 Http2Stream(nsAHttpTransaction*, Http2Session*, int32_t, uint64_t); 58 StreamID()59 uint32_t StreamID() { return mStreamID; } PushSource()60 Http2PushedStream* PushSource() { return mPushSource; } 61 void ClearPushSource(); 62 HTTPState()63 stateType HTTPState() { return mState; } SetHTTPState(stateType val)64 void SetHTTPState(stateType val) { mState = val; } 65 66 [[nodiscard]] virtual nsresult ReadSegments(nsAHttpSegmentReader*, uint32_t, 67 uint32_t*); 68 [[nodiscard]] virtual nsresult WriteSegments(nsAHttpSegmentWriter*, uint32_t, 69 uint32_t*); 70 virtual bool DeferCleanup(nsresult status); 71 72 // The consumer stream is the synthetic pull stream hooked up to this stream 73 // http2PushedStream overrides it GetConsumerStream()74 virtual Http2Stream* GetConsumerStream() { return nullptr; }; 75 Origin()76 const nsCString& Origin() const { return mOrigin; } Host()77 const nsCString& Host() const { return mHeaderHost; } Path()78 const nsCString& Path() const { return mHeaderPath; } 79 RequestBlockedOnRead()80 bool RequestBlockedOnRead() { 81 return static_cast<bool>(mRequestBlockedOnRead); 82 } 83 HasRegisteredID()84 bool HasRegisteredID() { return mStreamID != 0; } 85 Transaction()86 nsAHttpTransaction* Transaction() { return mTransaction; } RequestContext()87 virtual nsIRequestContext* RequestContext() { 88 return mTransaction ? mTransaction->RequestContext() : nullptr; 89 } 90 91 void Close(nsresult reason); 92 void SetResponseIsComplete(); 93 94 void SetRecvdFin(bool aStatus); RecvdFin()95 bool RecvdFin() { return mRecvdFin; } 96 SetRecvdData(bool aStatus)97 void SetRecvdData(bool aStatus) { mReceivedData = aStatus ? 1 : 0; } RecvdData()98 bool RecvdData() { return mReceivedData; } 99 100 void SetSentFin(bool aStatus); SentFin()101 bool SentFin() { return mSentFin; } 102 103 void SetRecvdReset(bool aStatus); RecvdReset()104 bool RecvdReset() { return mRecvdReset; } 105 106 void SetSentReset(bool aStatus); SentReset()107 bool SentReset() { return mSentReset; } 108 SetQueued(bool aStatus)109 void SetQueued(bool aStatus) { mQueued = aStatus ? 1 : 0; } Queued()110 bool Queued() { return mQueued; } 111 SetCountAsActive(bool aStatus)112 void SetCountAsActive(bool aStatus) { mCountAsActive = aStatus ? 1 : 0; } CountAsActive()113 bool CountAsActive() { return mCountAsActive; } 114 115 void SetAllHeadersReceived(); UnsetAllHeadersReceived()116 void UnsetAllHeadersReceived() { mAllHeadersReceived = 0; } AllHeadersReceived()117 bool AllHeadersReceived() { return mAllHeadersReceived; } 118 119 void UpdateTransportSendEvents(uint32_t count); 120 void UpdateTransportReadEvents(uint32_t count); 121 122 // NS_ERROR_ABORT terminates stream, other failure terminates session 123 [[nodiscard]] nsresult ConvertResponseHeaders(Http2Decompressor*, nsACString&, 124 nsACString&, int32_t&); 125 [[nodiscard]] nsresult ConvertPushHeaders(Http2Decompressor*, nsACString&, 126 nsACString&); 127 [[nodiscard]] nsresult ConvertResponseTrailers(Http2Decompressor*, 128 nsACString&); 129 130 bool AllowFlowControlledWrite(); 131 void UpdateServerReceiveWindow(int32_t delta); ServerReceiveWindow()132 int64_t ServerReceiveWindow() { return mServerReceiveWindow; } 133 DecrementClientReceiveWindow(uint32_t delta)134 void DecrementClientReceiveWindow(uint32_t delta) { 135 mClientReceiveWindow -= delta; 136 mLocalUnacked += delta; 137 } 138 IncrementClientReceiveWindow(uint32_t delta)139 void IncrementClientReceiveWindow(uint32_t delta) { 140 mClientReceiveWindow += delta; 141 mLocalUnacked -= delta; 142 } 143 144 uint64_t LocalUnAcked(); ClientReceiveWindow()145 int64_t ClientReceiveWindow() { return mClientReceiveWindow; } 146 BlockedOnRwin()147 bool BlockedOnRwin() { return mBlockedOnRwin; } 148 Priority()149 uint32_t Priority() { return mPriority; } PriorityDependency()150 uint32_t PriorityDependency() { return mPriorityDependency; } PriorityWeight()151 uint8_t PriorityWeight() { return mPriorityWeight; } 152 void SetPriority(uint32_t); 153 void SetPriorityDependency(uint32_t, uint32_t); 154 void UpdatePriorityDependency(); 155 TransactionTabId()156 uint64_t TransactionTabId() { return mTransactionTabId; } 157 158 // A pull stream has an implicit sink, a pushed stream has a sink 159 // once it is matched to a pull stream. HasSink()160 virtual bool HasSink() { return true; } 161 162 // This is a no-op on pull streams. Pushed streams override this. SetPushComplete()163 virtual void SetPushComplete(){}; 164 165 already_AddRefed<Http2Session> Session(); 166 167 [[nodiscard]] static nsresult MakeOriginURL(const nsACString& origin, 168 nsCOMPtr<nsIURI>& url); 169 170 [[nodiscard]] static nsresult MakeOriginURL(const nsACString& scheme, 171 const nsACString& origin, 172 nsCOMPtr<nsIURI>& url); 173 174 // Mirrors nsAHttpTransaction 175 bool Do0RTT(); 176 nsresult Finish0RTT(bool aRestart, bool aAlpnChanged); 177 178 nsresult GetOriginAttributes(mozilla::OriginAttributes* oa); 179 180 virtual void TopBrowsingContextIdChanged(uint64_t id); 181 void TopBrowsingContextIdChangedInternal( 182 uint64_t id); // For use by pushed streams only 183 184 protected: 185 virtual ~Http2Stream(); 186 static void CreatePushHashKey( 187 const nsCString& scheme, const nsCString& hostHeader, 188 const mozilla::OriginAttributes& originAttributes, uint64_t serial, 189 const nsACString& pathInfo, nsCString& outOrigin, nsCString& outKey); 190 191 // These internal states track request generation 192 enum upstreamStateType { 193 GENERATING_HEADERS, 194 GENERATING_BODY, 195 SENDING_BODY, 196 SENDING_FIN_STREAM, 197 UPSTREAM_COMPLETE 198 }; 199 200 uint32_t mStreamID; 201 202 // The session that this stream is a subset of 203 nsWeakPtr mSession; 204 205 // These are temporary state variables to hold the argument to 206 // Read/WriteSegments so it can be accessed by On(read/write)segment 207 // further up the stack. 208 RefPtr<nsAHttpSegmentReader> mSegmentReader; 209 nsAHttpSegmentWriter* mSegmentWriter; 210 211 nsCString mOrigin; 212 nsCString mHeaderHost; 213 nsCString mHeaderScheme; 214 nsCString mHeaderPath; 215 216 // Each stream goes from generating_headers to upstream_complete, perhaps 217 // looping on multiple instances of generating_body and 218 // sending_body for each frame in the upload. 219 enum upstreamStateType mUpstreamState; 220 221 // The HTTP/2 state for the stream from section 5.1 222 enum stateType mState; 223 224 // Flag is set when all http request headers have been read ID is not stable 225 uint32_t mRequestHeadersDone : 1; 226 227 // Flag is set when ID is stable and concurrency limits are met 228 uint32_t mOpenGenerated : 1; 229 230 // Flag is set when all http response headers have been read 231 uint32_t mAllHeadersReceived : 1; 232 233 // Flag is set when stream is queued inside the session due to 234 // concurrency limits being exceeded 235 uint32_t mQueued : 1; 236 237 void ChangeState(enum upstreamStateType); 238 239 virtual void AdjustInitialWindow(); 240 [[nodiscard]] nsresult TransmitFrame(const char*, uint32_t*, 241 bool forceCommitment); 242 243 // The underlying socket transport object is needed to propogate some events 244 nsISocketTransport* mSocketTransport; 245 246 uint8_t mPriorityWeight = 0; // h2 weight 247 uint32_t mPriorityDependency = 0; // h2 stream id this one depends on 248 uint64_t mCurrentTopBrowsingContextId; 249 uint64_t mTransactionTabId; 250 251 private: 252 friend class mozilla::DefaultDelete<Http2Stream>; 253 254 [[nodiscard]] nsresult ParseHttpRequestHeaders(const char*, uint32_t, 255 uint32_t*); 256 [[nodiscard]] nsresult GenerateOpen(); 257 258 void AdjustPushedPriority(); 259 void GenerateDataFrameHeader(uint32_t, bool); 260 261 [[nodiscard]] nsresult BufferInput(uint32_t, uint32_t*); 262 263 // The underlying HTTP transaction. This pointer is used as the key 264 // in the Http2Session mStreamTransactionHash so it is important to 265 // keep a reference to it as long as this stream is a member of that hash. 266 // (i.e. don't change it or release it after it is set in the ctor). 267 RefPtr<nsAHttpTransaction> mTransaction; 268 269 // The quanta upstream data frames are chopped into 270 uint32_t mChunkSize; 271 272 // Flag is set when the HTTP processor has more data to send 273 // but has blocked in doing so. 274 uint32_t mRequestBlockedOnRead : 1; 275 276 // Flag is set after the response frame bearing the fin bit has 277 // been processed. (i.e. after the server has closed). 278 uint32_t mRecvdFin : 1; 279 280 // Flag is set after 1st DATA frame has been passed to stream 281 uint32_t mReceivedData : 1; 282 283 // Flag is set after RST_STREAM has been received for this stream 284 uint32_t mRecvdReset : 1; 285 286 // Flag is set after RST_STREAM has been generated for this stream 287 uint32_t mSentReset : 1; 288 289 // Flag is set when stream is counted towards MAX_CONCURRENT streams in 290 // session 291 uint32_t mCountAsActive : 1; 292 293 // Flag is set when a FIN has been placed on a data or header frame 294 // (i.e after the client has closed) 295 uint32_t mSentFin : 1; 296 297 // Flag is set after the WAITING_FOR Transport event has been generated 298 uint32_t mSentWaitingFor : 1; 299 300 // Flag is set after TCP send autotuning has been disabled 301 uint32_t mSetTCPSocketBuffer : 1; 302 303 // Flag is set when OnWriteSegment is being called directly from stream 304 // instead of transaction 305 uint32_t mBypassInputBuffer : 1; 306 307 // The InlineFrame and associated data is used for composing control 308 // frames and data frame headers. 309 UniquePtr<uint8_t[]> mTxInlineFrame; 310 uint32_t mTxInlineFrameSize; 311 uint32_t mTxInlineFrameUsed; 312 313 // mTxStreamFrameSize tracks the progress of 314 // transmitting a request body data frame. The data frame itself 315 // is never copied into the spdy layer. 316 uint32_t mTxStreamFrameSize; 317 318 // Buffer for request header compression. 319 nsCString mFlatHttpRequestHeaders; 320 321 // Track the content-length of a request body so that we can 322 // place the fin flag on the last data packet instead of waiting 323 // for a stream closed indication. Relying on stream close results 324 // in an extra 0-length runt packet and seems to have some interop 325 // problems with the google servers. Connect does rely on stream 326 // close by setting this to the max value. 327 int64_t mRequestBodyLenRemaining; 328 329 uint32_t mPriority = 0; // geckoish weight 330 331 // mClientReceiveWindow, mServerReceiveWindow, and mLocalUnacked are for flow 332 // control. *window are signed because the race conditions in asynchronous 333 // SETTINGS messages can force them temporarily negative. 334 335 // mClientReceiveWindow is how much data the server will send without getting 336 // a 337 // window update 338 int64_t mClientReceiveWindow; 339 340 // mServerReceiveWindow is how much data the client is allowed to send without 341 // getting a window update 342 int64_t mServerReceiveWindow; 343 344 // LocalUnacked is the number of bytes received by the client but not 345 // yet reflected in a window update. Sending that update will increment 346 // ClientReceiveWindow 347 uint64_t mLocalUnacked; 348 349 // True when sending is suspended becuase the server receive window is 350 // <= 0 351 bool mBlockedOnRwin; 352 353 // For Progress Events 354 uint64_t mTotalSent; 355 uint64_t mTotalRead; 356 357 // For Http2Push 358 Http2PushedStream* mPushSource; 359 360 // Used to store stream data when the transaction channel cannot keep up 361 // and flow control has not yet kicked in. 362 SimpleBuffer mSimpleBuffer; 363 364 bool mAttempting0RTT; 365 366 /// connect tunnels 367 public: IsTunnel()368 bool IsTunnel() { return mIsTunnel; } 369 // TODO - remove as part of bug 1564120 fix? 370 // This method saves the key the tunnel was registered under, so that when the 371 // associated transaction connection info hash key changes, we still find it 372 // and decrement the correct item in the session's tunnel hash table. 373 nsCString& RegistrationKey(); 374 375 private: 376 void ClearTransactionsBlockedOnTunnel(); 377 void MapStreamToPlainText(); 378 bool MapStreamToHttpConnection(const nsACString& aFlat407Headers, 379 int32_t aHttpResponseCode = -1); 380 381 bool mIsTunnel; 382 bool mPlainTextTunnel; 383 nsCString mRegistrationKey; 384 385 /// websockets 386 public: IsWebsocket()387 bool IsWebsocket() { return mIsWebsocket; } 388 389 private: 390 bool mIsWebsocket; 391 }; 392 393 } // namespace net 394 } // namespace mozilla 395 396 #endif // mozilla_net_Http2Stream_h 397