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