1 /* -*- Mode: C++; tab-width: 2; 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_Http2Session_h 7 #define mozilla_net_Http2Session_h 8 9 // HTTP/2 - RFC 7540 10 // https://www.rfc-editor.org/rfc/rfc7540.txt 11 12 #include "ASpdySession.h" 13 #include "mozilla/Attributes.h" 14 #include "mozilla/UniquePtr.h" 15 #include "nsAHttpConnection.h" 16 #include "nsClassHashtable.h" 17 #include "nsDataHashtable.h" 18 #include "nsDeque.h" 19 #include "nsHashKeys.h" 20 21 #include "Http2Compression.h" 22 23 class nsISocketTransport; 24 25 namespace mozilla { 26 namespace net { 27 28 class Http2PushedStream; 29 class Http2Stream; 30 class nsHttpTransaction; 31 32 class Http2Session final : public ASpdySession 33 , public nsAHttpConnection 34 , public nsAHttpSegmentReader 35 , public nsAHttpSegmentWriter 36 { 37 ~Http2Session(); 38 39 public: 40 NS_DECL_THREADSAFE_ISUPPORTS 41 NS_DECL_NSAHTTPTRANSACTION 42 NS_DECL_NSAHTTPCONNECTION(mConnection) 43 NS_DECL_NSAHTTPSEGMENTREADER 44 NS_DECL_NSAHTTPSEGMENTWRITER 45 46 Http2Session(nsISocketTransport *, uint32_t version); 47 48 bool AddStream(nsAHttpTransaction *, int32_t, 49 bool, nsIInterfaceRequestor *) override; CanReuse()50 bool CanReuse() override { return !mShouldGoAway && !mClosed; } 51 bool RoomForMoreStreams() override; 52 53 // When the connection is active this is called up to once every 1 second 54 // return the interval (in seconds) that the connection next wants to 55 // have this invoked. It might happen sooner depending on the needs of 56 // other connections. 57 uint32_t ReadTimeoutTick(PRIntervalTime now) override; 58 59 // Idle time represents time since "goodput".. e.g. a data or header frame 60 PRIntervalTime IdleTime() override; 61 62 // Registering with a newID of 0 means pick the next available odd ID 63 uint32_t RegisterStreamID(Http2Stream *, uint32_t aNewID = 0); 64 65 /* 66 HTTP/2 framing 67 68 0 1 2 3 69 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 70 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 71 | Length (16) | Type (8) | Flags (8) | 72 +-+-------------+---------------+-------------------------------+ 73 |R| Stream Identifier (31) | 74 +-+-------------------------------------------------------------+ 75 | Frame Data (0...) ... 76 +---------------------------------------------------------------+ 77 */ 78 79 enum FrameType { 80 FRAME_TYPE_DATA = 0x0, 81 FRAME_TYPE_HEADERS = 0x1, 82 FRAME_TYPE_PRIORITY = 0x2, 83 FRAME_TYPE_RST_STREAM = 0x3, 84 FRAME_TYPE_SETTINGS = 0x4, 85 FRAME_TYPE_PUSH_PROMISE = 0x5, 86 FRAME_TYPE_PING = 0x6, 87 FRAME_TYPE_GOAWAY = 0x7, 88 FRAME_TYPE_WINDOW_UPDATE = 0x8, 89 FRAME_TYPE_CONTINUATION = 0x9, 90 FRAME_TYPE_ALTSVC = 0xA, 91 FRAME_TYPE_LAST = 0xB 92 }; 93 94 // NO_ERROR is a macro defined on windows, so we'll name the HTTP2 goaway 95 // code NO_ERROR to be NO_HTTP_ERROR 96 enum errorType { 97 NO_HTTP_ERROR = 0, 98 PROTOCOL_ERROR = 1, 99 INTERNAL_ERROR = 2, 100 FLOW_CONTROL_ERROR = 3, 101 SETTINGS_TIMEOUT_ERROR = 4, 102 STREAM_CLOSED_ERROR = 5, 103 FRAME_SIZE_ERROR = 6, 104 REFUSED_STREAM_ERROR = 7, 105 CANCEL_ERROR = 8, 106 COMPRESSION_ERROR = 9, 107 CONNECT_ERROR = 10, 108 ENHANCE_YOUR_CALM = 11, 109 INADEQUATE_SECURITY = 12, 110 HTTP_1_1_REQUIRED = 13, 111 UNASSIGNED = 31 112 }; 113 114 // These are frame flags. If they, or other undefined flags, are 115 // used on frames other than the comments indicate they MUST be ignored. 116 const static uint8_t kFlag_END_STREAM = 0x01; // data, headers 117 const static uint8_t kFlag_END_HEADERS = 0x04; // headers, continuation 118 const static uint8_t kFlag_END_PUSH_PROMISE = 0x04; // push promise 119 const static uint8_t kFlag_ACK = 0x01; // ping and settings 120 const static uint8_t kFlag_PADDED = 0x08; // data, headers, push promise, continuation 121 const static uint8_t kFlag_PRIORITY = 0x20; // headers 122 123 enum { 124 SETTINGS_TYPE_HEADER_TABLE_SIZE = 1, // compression table size 125 SETTINGS_TYPE_ENABLE_PUSH = 2, // can be used to disable push 126 SETTINGS_TYPE_MAX_CONCURRENT = 3, // streams recvr allowed to initiate 127 SETTINGS_TYPE_INITIAL_WINDOW = 4, // bytes for flow control default 128 SETTINGS_TYPE_MAX_FRAME_SIZE = 5 // max frame size settings sender allows receipt of 129 }; 130 131 // This should be big enough to hold all of your control packets, 132 // but if it needs to grow for huge headers it can do so dynamically. 133 const static uint32_t kDefaultBufferSize = 2048; 134 135 // kDefaultQueueSize must be >= other queue size constants 136 const static uint32_t kDefaultQueueSize = 32768; 137 const static uint32_t kQueueMinimumCleanup = 24576; 138 const static uint32_t kQueueTailRoom = 4096; 139 const static uint32_t kQueueReserved = 1024; 140 141 const static uint32_t kMaxStreamID = 0x7800000; 142 143 // This is a sentinel for a deleted stream. It is not a valid 144 // 31 bit stream ID. 145 const static uint32_t kDeadStreamID = 0xffffdead; 146 147 // below the emergency threshold of local window we ack every received 148 // byte. Above that we coalesce bytes into the MinimumToAck size. 149 const static int32_t kEmergencyWindowThreshold = 256 * 1024; 150 const static uint32_t kMinimumToAck = 4 * 1024 * 1024; 151 152 // The default rwin is 64KB - 1 unless updated by a settings frame 153 const static uint32_t kDefaultRwin = 65535; 154 155 // We limit frames to 2^14 bytes of length in order to preserve responsiveness 156 // This is the smallest allowed value for SETTINGS_MAX_FRAME_SIZE 157 const static uint32_t kMaxFrameData = 0x4000; 158 159 const static uint8_t kFrameLengthBytes = 3; 160 const static uint8_t kFrameStreamIDBytes = 4; 161 const static uint8_t kFrameFlagBytes = 1; 162 const static uint8_t kFrameTypeBytes = 1; 163 const static uint8_t kFrameHeaderBytes = kFrameLengthBytes + kFrameFlagBytes + 164 kFrameTypeBytes + kFrameStreamIDBytes; 165 166 enum { 167 kLeaderGroupID = 0x3, 168 kOtherGroupID = 0x5, 169 kBackgroundGroupID = 0x7, 170 kSpeculativeGroupID = 0x9, 171 kFollowerGroupID = 0xB 172 }; 173 174 static nsresult RecvHeaders(Http2Session *); 175 static nsresult RecvPriority(Http2Session *); 176 static nsresult RecvRstStream(Http2Session *); 177 static nsresult RecvSettings(Http2Session *); 178 static nsresult RecvPushPromise(Http2Session *); 179 static nsresult RecvPing(Http2Session *); 180 static nsresult RecvGoAway(Http2Session *); 181 static nsresult RecvWindowUpdate(Http2Session *); 182 static nsresult RecvContinuation(Http2Session *); 183 static nsresult RecvAltSvc(Http2Session *); 184 185 char *EnsureOutputBuffer(uint32_t needed); 186 187 template<typename charType> 188 void CreateFrameHeader(charType dest, uint16_t frameLength, 189 uint8_t frameType, uint8_t frameFlags, 190 uint32_t streamID); 191 192 // For writing the data stream to LOG4 193 static void LogIO(Http2Session *, Http2Stream *, const char *, 194 const char *, uint32_t); 195 196 // overload of nsAHttpConnection 197 void TransactionHasDataToWrite(nsAHttpTransaction *) override; 198 void TransactionHasDataToRecv(nsAHttpTransaction *) override; 199 200 // a similar version for Http2Stream 201 void TransactionHasDataToWrite(Http2Stream *); 202 203 // an overload of nsAHttpSegementReader 204 virtual nsresult CommitToSegmentSize(uint32_t size, bool forceCommitment) override; 205 nsresult BufferOutput(const char *, uint32_t, uint32_t *); 206 void FlushOutputQueue(); AmountOfOutputBuffered()207 uint32_t AmountOfOutputBuffered() { return mOutputQueueUsed - mOutputQueueSent; } 208 GetServerInitialStreamWindow()209 uint32_t GetServerInitialStreamWindow() { return mServerInitialStreamWindow; } 210 211 bool TryToActivate(Http2Stream *stream); 212 void ConnectPushedStream(Http2Stream *stream); 213 void ConnectSlowConsumer(Http2Stream *stream); 214 215 nsresult ConfirmTLSProfile(); 216 static bool ALPNCallback(nsISupports *securityInfo); 217 Serial()218 uint64_t Serial() { return mSerial; } 219 220 void PrintDiagnostics (nsCString &log) override; 221 222 // Streams need access to these SendingChunkSize()223 uint32_t SendingChunkSize() { return mSendingChunkSize; } PushAllowance()224 uint32_t PushAllowance() { return mPushAllowance; } Compressor()225 Http2Compressor *Compressor() { return &mCompressor; } SocketTransport()226 nsISocketTransport *SocketTransport() { return mSocketTransport; } ServerSessionWindow()227 int64_t ServerSessionWindow() { return mServerSessionWindow; } DecrementServerSessionWindow(uint32_t bytes)228 void DecrementServerSessionWindow (uint32_t bytes) { mServerSessionWindow -= bytes; } InitialRwin()229 uint32_t InitialRwin() { return mInitialRwin; } 230 231 void SendPing() override; 232 bool MaybeReTunnel(nsAHttpTransaction *) override; UseH2Deps()233 bool UseH2Deps() { return mUseH2Deps; } 234 235 // overload of nsAHttpTransaction 236 nsresult ReadSegmentsAgain(nsAHttpSegmentReader *, uint32_t, uint32_t *, bool *) override final; 237 nsresult WriteSegmentsAgain(nsAHttpSegmentWriter *, uint32_t , uint32_t *, bool *) override final; 238 239 private: 240 241 // These internal states do not correspond to the states of the HTTP/2 specification 242 enum internalStateType { 243 BUFFERING_OPENING_SETTINGS, 244 BUFFERING_FRAME_HEADER, 245 BUFFERING_CONTROL_FRAME, 246 PROCESSING_DATA_FRAME_PADDING_CONTROL, 247 PROCESSING_DATA_FRAME, 248 DISCARDING_DATA_FRAME_PADDING, 249 DISCARDING_DATA_FRAME, 250 PROCESSING_COMPLETE_HEADERS, 251 PROCESSING_CONTROL_RST_STREAM, 252 NOT_USING_NETWORK 253 }; 254 255 static const uint8_t kMagicHello[24]; 256 257 nsresult ResponseHeadersComplete(); 258 uint32_t GetWriteQueueSize(); 259 void ChangeDownstreamState(enum internalStateType); 260 void ResetDownstreamState(); 261 nsresult ReadyToProcessDataFrame(enum internalStateType); 262 nsresult UncompressAndDiscard(bool); 263 void GeneratePing(bool); 264 void GenerateSettingsAck(); 265 void GeneratePriority(uint32_t, uint8_t); 266 void GenerateRstStream(uint32_t, uint32_t); 267 void GenerateGoAway(uint32_t); 268 void CleanupStream(Http2Stream *, nsresult, errorType); 269 void CleanupStream(uint32_t, nsresult, errorType); 270 void CloseStream(Http2Stream *, nsresult); 271 void SendHello(); 272 void RemoveStreamFromQueues(Http2Stream *); 273 nsresult ParsePadding(uint8_t &, uint16_t &); 274 275 void SetWriteCallbacks(); 276 void RealignOutputQueue(); 277 278 void ProcessPending(); 279 nsresult ProcessConnectedPush(Http2Stream *, nsAHttpSegmentWriter *, 280 uint32_t, uint32_t *); 281 nsresult ProcessSlowConsumer(Http2Stream *, nsAHttpSegmentWriter *, 282 uint32_t, uint32_t *); 283 284 nsresult SetInputFrameDataStream(uint32_t); 285 void CreatePriorityNode(uint32_t, uint32_t, uint8_t, const char *); 286 bool VerifyStream(Http2Stream *, uint32_t); 287 void SetNeedsCleanup(); 288 289 void UpdateLocalRwin(Http2Stream *stream, uint32_t bytes); 290 void UpdateLocalStreamWindow(Http2Stream *stream, uint32_t bytes); 291 void UpdateLocalSessionWindow(uint32_t bytes); 292 293 void MaybeDecrementConcurrent(Http2Stream *stream); 294 bool RoomForMoreConcurrent(); 295 void IncrementConcurrent(Http2Stream *stream); 296 void QueueStream(Http2Stream *stream); 297 298 // a wrapper for all calls to the nshttpconnection level segment writer. Used 299 // to track network I/O for timeout purposes 300 nsresult NetworkRead(nsAHttpSegmentWriter *, char *, uint32_t, uint32_t *); 301 302 void Shutdown(); 303 304 // This is intended to be nsHttpConnectionMgr:nsConnectionHandle taken 305 // from the first transaction on this session. That object contains the 306 // pointer to the real network-level nsHttpConnection object. 307 RefPtr<nsAHttpConnection> mConnection; 308 309 // The underlying socket transport object is needed to propogate some events 310 nsISocketTransport *mSocketTransport; 311 312 // These are temporary state variables to hold the argument to 313 // Read/WriteSegments so it can be accessed by On(read/write)segment 314 // further up the stack. 315 nsAHttpSegmentReader *mSegmentReader; 316 nsAHttpSegmentWriter *mSegmentWriter; 317 318 uint32_t mSendingChunkSize; /* the transmission chunk size */ 319 uint32_t mNextStreamID; /* 24 bits */ 320 uint32_t mLastPushedID; 321 uint32_t mConcurrentHighWater; /* max parallelism on session */ 322 uint32_t mPushAllowance; /* rwin for unmatched pushes */ 323 324 internalStateType mDownstreamState; /* in frame, between frames, etc.. */ 325 326 // Maintain 2 indexes - one by stream ID, one by transaction pointer. 327 // There are also several lists of streams: ready to write, queued due to 328 // max parallelism, streams that need to force a read for push, and the full 329 // set of pushed streams. 330 // The objects are not ref counted - they get destroyed 331 // by the nsClassHashtable implementation when they are removed from 332 // the transaction hash. 333 nsDataHashtable<nsUint32HashKey, Http2Stream *> mStreamIDHash; 334 nsClassHashtable<nsPtrHashKey<nsAHttpTransaction>, 335 Http2Stream> mStreamTransactionHash; 336 337 nsDeque mReadyForWrite; 338 nsDeque mQueuedStreams; 339 nsDeque mPushesReadyForRead; 340 nsDeque mSlowConsumersReadyForRead; 341 nsTArray<Http2PushedStream *> mPushedStreams; 342 343 // Compression contexts for header transport. 344 // HTTP/2 compresses only HTTP headers and does not reset the context in between 345 // frames. Even data that is not associated with a stream (e.g invalid 346 // stream ID) is passed through these contexts to keep the compression 347 // context correct. 348 Http2Compressor mCompressor; 349 Http2Decompressor mDecompressor; 350 nsCString mDecompressBuffer; 351 352 // mInputFrameBuffer is used to store received control packets and the 8 bytes 353 // of header on data packets 354 uint32_t mInputFrameBufferSize; // buffer allocation 355 uint32_t mInputFrameBufferUsed; // amt of allocation used 356 UniquePtr<char[]> mInputFrameBuffer; 357 358 // mInputFrameDataSize/Read are used for tracking the amount of data consumed 359 // in a frame after the 8 byte header. Control frames are always fully buffered 360 // and the fixed 8 byte leading header is at mInputFrameBuffer + 0, the first 361 // data byte (i.e. the first settings/goaway/etc.. specific byte) is at 362 // mInputFrameBuffer + 8 363 // The frame size is mInputFrameDataSize + the constant 8 byte header 364 uint32_t mInputFrameDataSize; 365 uint32_t mInputFrameDataRead; 366 bool mInputFrameFinal; // This frame was marked FIN 367 uint8_t mInputFrameType; 368 uint8_t mInputFrameFlags; 369 uint32_t mInputFrameID; 370 uint16_t mPaddingLength; 371 372 // When a frame has been received that is addressed to a particular stream 373 // (e.g. a data frame after the stream-id has been decoded), this points 374 // to the stream. 375 Http2Stream *mInputFrameDataStream; 376 377 // mNeedsCleanup is a state variable to defer cleanup of a closed stream 378 // If needed, It is set in session::OnWriteSegments() and acted on and 379 // cleared when the stack returns to session::WriteSegments(). The stream 380 // cannot be destroyed directly out of OnWriteSegments because 381 // stream::writeSegments() is on the stack at that time. 382 Http2Stream *mNeedsCleanup; 383 384 // This reason code in the last processed RESET frame 385 uint32_t mDownstreamRstReason; 386 387 // When HEADERS/PROMISE are chained together, this is the expected ID of the next 388 // recvd frame which must be the same type 389 uint32_t mExpectedHeaderID; 390 uint32_t mExpectedPushPromiseID; 391 uint32_t mContinuedPromiseStream; 392 393 // for the conversion of downstream http headers into http/2 formatted headers 394 // The data here does not persist between frames 395 nsCString mFlatHTTPResponseHeaders; 396 uint32_t mFlatHTTPResponseHeadersOut; 397 398 // when set, the session will go away when it reaches 0 streams. This flag 399 // is set when: the stream IDs are running out (at either the client or the 400 // server), when DontReuse() is called, a RST that is not specific to a 401 // particular stream is received, a GOAWAY frame has been received from 402 // the server. 403 bool mShouldGoAway; 404 405 // the session has received a nsAHttpTransaction::Close() call 406 bool mClosed; 407 408 // the session received a GoAway frame with a valid GoAwayID 409 bool mCleanShutdown; 410 411 // The TLS comlpiance checks are not done in the ctor beacuse of bad 412 // exception handling - so we do them at IO time and cache the result 413 bool mTLSProfileConfirmed; 414 415 // A specifc reason code for the eventual GoAway frame. If set to NO_HTTP_ERROR 416 // only NO_HTTP_ERROR, PROTOCOL_ERROR, or INTERNAL_ERROR will be sent. 417 errorType mGoAwayReason; 418 419 // The error code sent/received on the session goaway frame. UNASSIGNED/31 420 // if not transmitted. 421 int32_t mClientGoAwayReason; 422 int32_t mPeerGoAwayReason; 423 424 // If a GoAway message was received this is the ID of the last valid 425 // stream. 0 otherwise. (0 is never a valid stream id.) 426 uint32_t mGoAwayID; 427 428 // The last stream processed ID we will send in our GoAway frame. 429 uint32_t mOutgoingGoAwayID; 430 431 // The limit on number of concurrent streams for this session. Normally it 432 // is basically unlimited, but the SETTINGS control message from the 433 // server might bring it down. 434 uint32_t mMaxConcurrent; 435 436 // The actual number of concurrent streams at this moment. Generally below 437 // mMaxConcurrent, but the max can be lowered in real time to a value 438 // below the current value 439 uint32_t mConcurrent; 440 441 // The number of server initiated promises, tracked for telemetry 442 uint32_t mServerPushedResources; 443 444 // The server rwin for new streams as determined from a SETTINGS frame 445 uint32_t mServerInitialStreamWindow; 446 447 // The Local Session window is how much data the server is allowed to send 448 // (across all streams) without getting a window update to stream 0. It is 449 // signed because asynchronous changes via SETTINGS can drive it negative. 450 int64_t mLocalSessionWindow; 451 452 // The Remote Session Window is how much data the client is allowed to send 453 // (across all streams) without receiving a window update to stream 0. It is 454 // signed because asynchronous changes via SETTINGS can drive it negative. 455 int64_t mServerSessionWindow; 456 457 // The initial value of the local stream and session window 458 uint32_t mInitialRwin; 459 460 // This is a output queue of bytes ready to be written to the SSL stream. 461 // When that streams returns WOULD_BLOCK on direct write the bytes get 462 // coalesced together here. This results in larger writes to the SSL layer. 463 // The buffer is not dynamically grown to accomodate stream writes, but 464 // does expand to accept infallible session wide frames like GoAway and RST. 465 uint32_t mOutputQueueSize; 466 uint32_t mOutputQueueUsed; 467 uint32_t mOutputQueueSent; 468 UniquePtr<char[]> mOutputQueueBuffer; 469 470 PRIntervalTime mPingThreshold; 471 PRIntervalTime mLastReadEpoch; // used for ping timeouts 472 PRIntervalTime mLastDataReadEpoch; // used for IdleTime() 473 PRIntervalTime mPingSentEpoch; 474 475 PRIntervalTime mPreviousPingThreshold; // backup for the former value 476 bool mPreviousUsed; // true when backup is used 477 478 // used as a temporary buffer while enumerating the stream hash during GoAway 479 nsDeque mGoAwayStreamsToRestart; 480 481 // Each session gets a unique serial number because the push cache is correlated 482 // by the load group and the serial number can be used as part of the cache key 483 // to make sure streams aren't shared across sessions. 484 uint64_t mSerial; 485 486 // If push is disabled, we want to be able to send PROTOCOL_ERRORs if we 487 // receive a PUSH_PROMISE, but we have to wait for the SETTINGS ACK before 488 // we can actually tell the other end to go away. These help us keep track 489 // of that state so we can behave appropriately. 490 bool mWaitingForSettingsAck; 491 bool mGoAwayOnPush; 492 493 bool mUseH2Deps; 494 495 private: 496 /// connect tunnels 497 void DispatchOnTunnel(nsAHttpTransaction *, nsIInterfaceRequestor *); 498 void CreateTunnel(nsHttpTransaction *, nsHttpConnectionInfo *, nsIInterfaceRequestor *); 499 void RegisterTunnel(Http2Stream *); 500 void UnRegisterTunnel(Http2Stream *); 501 uint32_t FindTunnelCount(nsHttpConnectionInfo *); 502 nsDataHashtable<nsCStringHashKey, uint32_t> mTunnelHash; 503 }; 504 505 } // namespace net 506 } // namespace mozilla 507 508 #endif // mozilla_net_Http2Session_h 509