1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/netwerk/protocol/http/Http2Session.h Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,449 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#ifndef mozilla_net_Http2Session_h 1.10 +#define mozilla_net_Http2Session_h 1.11 + 1.12 +// HTTP/2 1.13 + 1.14 +#include "ASpdySession.h" 1.15 +#include "mozilla/Attributes.h" 1.16 +#include "nsAHttpConnection.h" 1.17 +#include "nsClassHashtable.h" 1.18 +#include "nsDataHashtable.h" 1.19 +#include "nsDeque.h" 1.20 +#include "nsHashKeys.h" 1.21 + 1.22 +#include "Http2Compression.h" 1.23 + 1.24 +class nsISocketTransport; 1.25 + 1.26 +namespace mozilla { 1.27 +namespace net { 1.28 + 1.29 +class Http2PushedStream; 1.30 +class Http2Stream; 1.31 + 1.32 +class Http2Session MOZ_FINAL : public ASpdySession 1.33 + , public nsAHttpConnection 1.34 + , public nsAHttpSegmentReader 1.35 + , public nsAHttpSegmentWriter 1.36 +{ 1.37 +public: 1.38 + NS_DECL_ISUPPORTS 1.39 + NS_DECL_NSAHTTPTRANSACTION 1.40 + NS_DECL_NSAHTTPCONNECTION(mConnection) 1.41 + NS_DECL_NSAHTTPSEGMENTREADER 1.42 + NS_DECL_NSAHTTPSEGMENTWRITER 1.43 + 1.44 + Http2Session(nsAHttpTransaction *, nsISocketTransport *, int32_t); 1.45 + ~Http2Session(); 1.46 + 1.47 + bool AddStream(nsAHttpTransaction *, int32_t); 1.48 + bool CanReuse() { return !mShouldGoAway && !mClosed; } 1.49 + bool RoomForMoreStreams(); 1.50 + 1.51 + // When the connection is active this is called up to once every 1 second 1.52 + // return the interval (in seconds) that the connection next wants to 1.53 + // have this invoked. It might happen sooner depending on the needs of 1.54 + // other connections. 1.55 + uint32_t ReadTimeoutTick(PRIntervalTime now); 1.56 + 1.57 + // Idle time represents time since "goodput".. e.g. a data or header frame 1.58 + PRIntervalTime IdleTime(); 1.59 + 1.60 + // Registering with a newID of 0 means pick the next available odd ID 1.61 + uint32_t RegisterStreamID(Http2Stream *, uint32_t aNewID = 0); 1.62 + 1.63 +/* 1.64 + HTTP/2 framing 1.65 + 1.66 + 0 1 2 3 1.67 + 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 1.68 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 1.69 + | Length (16) | Type (8) | Flags (8) | 1.70 + +-+-------------+---------------+-------------------------------+ 1.71 + |R| Stream Identifier (31) | 1.72 + +-+-------------------------------------------------------------+ 1.73 + | Frame Data (0...) ... 1.74 + +---------------------------------------------------------------+ 1.75 +*/ 1.76 + 1.77 + enum frameType { 1.78 + FRAME_TYPE_DATA = 0, 1.79 + FRAME_TYPE_HEADERS = 1, 1.80 + FRAME_TYPE_PRIORITY = 2, 1.81 + FRAME_TYPE_RST_STREAM = 3, 1.82 + FRAME_TYPE_SETTINGS = 4, 1.83 + FRAME_TYPE_PUSH_PROMISE = 5, 1.84 + FRAME_TYPE_PING = 6, 1.85 + FRAME_TYPE_GOAWAY = 7, 1.86 + FRAME_TYPE_WINDOW_UPDATE = 8, 1.87 + FRAME_TYPE_CONTINUATION = 9, 1.88 + FRAME_TYPE_LAST = 10 1.89 + }; 1.90 + 1.91 + // NO_ERROR is a macro defined on windows, so we'll name the HTTP2 goaway 1.92 + // code NO_ERROR to be NO_HTTP_ERROR 1.93 + enum errorType { 1.94 + NO_HTTP_ERROR = 0, 1.95 + PROTOCOL_ERROR = 1, 1.96 + INTERNAL_ERROR = 2, 1.97 + FLOW_CONTROL_ERROR = 3, 1.98 + SETTINGS_TIMEOUT_ERROR = 4, 1.99 + STREAM_CLOSED_ERROR = 5, 1.100 + FRAME_SIZE_ERROR = 6, 1.101 + REFUSED_STREAM_ERROR = 7, 1.102 + CANCEL_ERROR = 8, 1.103 + COMPRESSION_ERROR = 9, 1.104 + CONNECT_ERROR = 10, 1.105 + ENHANCE_YOUR_CALM = 11, 1.106 + INADEQUATE_SECURITY = 12 1.107 + }; 1.108 + 1.109 + // These are frame flags. If they, or other undefined flags, are 1.110 + // used on frames other than the comments indicate they MUST be ignored. 1.111 + const static uint8_t kFlag_END_STREAM = 0x01; // data, headers 1.112 + const static uint8_t kFlag_END_HEADERS = 0x04; // headers, continuation 1.113 + const static uint8_t kFlag_PRIORITY = 0x08; //headers 1.114 + const static uint8_t kFlag_END_PUSH_PROMISE = 0x04; // push promise 1.115 + const static uint8_t kFlag_ACK = 0x01; // ping and settings 1.116 + const static uint8_t kFlag_END_SEGMENT = 0x02; // data 1.117 + const static uint8_t kFlag_PAD_LOW = 0x10; // data, headers, continuation 1.118 + const static uint8_t kFlag_PAD_HIGH = 0x20; // data, headers, continuation 1.119 + 1.120 + enum { 1.121 + SETTINGS_TYPE_HEADER_TABLE_SIZE = 1, // compression table size 1.122 + SETTINGS_TYPE_ENABLE_PUSH = 2, // can be used to disable push 1.123 + SETTINGS_TYPE_MAX_CONCURRENT = 3, // streams recvr allowed to initiate 1.124 + SETTINGS_TYPE_INITIAL_WINDOW = 4 // bytes for flow control default 1.125 + }; 1.126 + 1.127 + // This should be big enough to hold all of your control packets, 1.128 + // but if it needs to grow for huge headers it can do so dynamically. 1.129 + const static uint32_t kDefaultBufferSize = 2048; 1.130 + 1.131 + // kDefaultQueueSize must be >= other queue size constants 1.132 + const static uint32_t kDefaultQueueSize = 32768; 1.133 + const static uint32_t kQueueMinimumCleanup = 24576; 1.134 + const static uint32_t kQueueTailRoom = 4096; 1.135 + const static uint32_t kQueueReserved = 1024; 1.136 + 1.137 + const static uint32_t kDefaultMaxConcurrent = 100; 1.138 + const static uint32_t kMaxStreamID = 0x7800000; 1.139 + 1.140 + // This is a sentinel for a deleted stream. It is not a valid 1.141 + // 31 bit stream ID. 1.142 + const static uint32_t kDeadStreamID = 0xffffdead; 1.143 + 1.144 + // below the emergency threshold of local window we ack every received 1.145 + // byte. Above that we coalesce bytes into the MinimumToAck size. 1.146 + const static int32_t kEmergencyWindowThreshold = 256 * 1024; 1.147 + const static uint32_t kMinimumToAck = 4 * 1024 * 1024; 1.148 + 1.149 + // The default rwin is 64KB - 1 unless updated by a settings frame 1.150 + const static uint32_t kDefaultRwin = 65535; 1.151 + 1.152 + // Frames with HTTP semantics are limited to 2^14 - 1 bytes of length in 1.153 + // order to preserve responsiveness 1.154 + const static uint32_t kMaxFrameData = 16383; 1.155 + 1.156 + static nsresult RecvHeaders(Http2Session *); 1.157 + static nsresult RecvPriority(Http2Session *); 1.158 + static nsresult RecvRstStream(Http2Session *); 1.159 + static nsresult RecvSettings(Http2Session *); 1.160 + static nsresult RecvPushPromise(Http2Session *); 1.161 + static nsresult RecvPing(Http2Session *); 1.162 + static nsresult RecvGoAway(Http2Session *); 1.163 + static nsresult RecvWindowUpdate(Http2Session *); 1.164 + static nsresult RecvContinuation(Http2Session *); 1.165 + 1.166 + template<typename T> 1.167 + static void EnsureBuffer(nsAutoArrayPtr<T> &, 1.168 + uint32_t, uint32_t, uint32_t &); 1.169 + char *EnsureOutputBuffer(uint32_t needed); 1.170 + 1.171 + template<typename charType> 1.172 + void CreateFrameHeader(charType dest, uint16_t frameLength, 1.173 + uint8_t frameType, uint8_t frameFlags, 1.174 + uint32_t streamID); 1.175 + 1.176 + // For writing the data stream to LOG4 1.177 + static void LogIO(Http2Session *, Http2Stream *, const char *, 1.178 + const char *, uint32_t); 1.179 + 1.180 + // an overload of nsAHttpConnection 1.181 + void TransactionHasDataToWrite(nsAHttpTransaction *); 1.182 + 1.183 + // a similar version for Http2Stream 1.184 + void TransactionHasDataToWrite(Http2Stream *); 1.185 + 1.186 + // an overload of nsAHttpSegementReader 1.187 + virtual nsresult CommitToSegmentSize(uint32_t size, bool forceCommitment); 1.188 + nsresult BufferOutput(const char *, uint32_t, uint32_t *); 1.189 + void FlushOutputQueue(); 1.190 + uint32_t AmountOfOutputBuffered() { return mOutputQueueUsed - mOutputQueueSent; } 1.191 + 1.192 + uint32_t GetServerInitialStreamWindow() { return mServerInitialStreamWindow; } 1.193 + 1.194 + void ConnectPushedStream(Http2Stream *stream); 1.195 + void MaybeDecrementConcurrent(Http2Stream *stream); 1.196 + 1.197 + nsresult ConfirmTLSProfile(); 1.198 + 1.199 + uint64_t Serial() { return mSerial; } 1.200 + 1.201 + void PrintDiagnostics (nsCString &log); 1.202 + 1.203 + // Streams need access to these 1.204 + uint32_t SendingChunkSize() { return mSendingChunkSize; } 1.205 + uint32_t PushAllowance() { return mPushAllowance; } 1.206 + Http2Compressor *Compressor() { return &mCompressor; } 1.207 + nsISocketTransport *SocketTransport() { return mSocketTransport; } 1.208 + int64_t ServerSessionWindow() { return mServerSessionWindow; } 1.209 + void DecrementServerSessionWindow (uint32_t bytes) { mServerSessionWindow -= bytes; } 1.210 + 1.211 +private: 1.212 + 1.213 + // These internal states do not correspond to the states of the HTTP/2 specification 1.214 + enum internalStateType { 1.215 + BUFFERING_OPENING_SETTINGS, 1.216 + BUFFERING_FRAME_HEADER, 1.217 + BUFFERING_CONTROL_FRAME, 1.218 + PROCESSING_DATA_FRAME_PADDING_CONTROL, 1.219 + PROCESSING_DATA_FRAME, 1.220 + DISCARDING_DATA_FRAME_PADDING, 1.221 + DISCARDING_DATA_FRAME, 1.222 + PROCESSING_COMPLETE_HEADERS, 1.223 + PROCESSING_CONTROL_RST_STREAM 1.224 + }; 1.225 + 1.226 + static const uint8_t kMagicHello[24]; 1.227 + 1.228 + nsresult ResponseHeadersComplete(); 1.229 + uint32_t GetWriteQueueSize(); 1.230 + void ChangeDownstreamState(enum internalStateType); 1.231 + void ResetDownstreamState(); 1.232 + nsresult ReadyToProcessDataFrame(enum internalStateType); 1.233 + nsresult UncompressAndDiscard(); 1.234 + void GeneratePing(bool); 1.235 + void GenerateSettingsAck(); 1.236 + void GeneratePriority(uint32_t, uint32_t); 1.237 + void GenerateRstStream(uint32_t, uint32_t); 1.238 + void GenerateGoAway(uint32_t); 1.239 + void CleanupStream(Http2Stream *, nsresult, errorType); 1.240 + void CloseStream(Http2Stream *, nsresult); 1.241 + void SendHello(); 1.242 + void RemoveStreamFromQueues(Http2Stream *); 1.243 + nsresult ParsePadding(uint8_t &, uint16_t &); 1.244 + 1.245 + void SetWriteCallbacks(); 1.246 + void RealignOutputQueue(); 1.247 + 1.248 + bool RoomForMoreConcurrent(); 1.249 + void ActivateStream(Http2Stream *); 1.250 + void ProcessPending(); 1.251 + nsresult SetInputFrameDataStream(uint32_t); 1.252 + bool VerifyStream(Http2Stream *, uint32_t); 1.253 + void SetNeedsCleanup(); 1.254 + 1.255 + void UpdateLocalRwin(Http2Stream *stream, uint32_t bytes); 1.256 + void UpdateLocalStreamWindow(Http2Stream *stream, uint32_t bytes); 1.257 + void UpdateLocalSessionWindow(uint32_t bytes); 1.258 + 1.259 + // a wrapper for all calls to the nshttpconnection level segment writer. Used 1.260 + // to track network I/O for timeout purposes 1.261 + nsresult NetworkRead(nsAHttpSegmentWriter *, char *, uint32_t, uint32_t *); 1.262 + 1.263 + static PLDHashOperator ShutdownEnumerator(nsAHttpTransaction *, 1.264 + nsAutoPtr<Http2Stream> &, 1.265 + void *); 1.266 + 1.267 + static PLDHashOperator GoAwayEnumerator(nsAHttpTransaction *, 1.268 + nsAutoPtr<Http2Stream> &, 1.269 + void *); 1.270 + 1.271 + static PLDHashOperator UpdateServerRwinEnumerator(nsAHttpTransaction *, 1.272 + nsAutoPtr<Http2Stream> &, 1.273 + void *); 1.274 + 1.275 + static PLDHashOperator RestartBlockedOnRwinEnumerator(nsAHttpTransaction *, 1.276 + nsAutoPtr<Http2Stream> &, 1.277 + void *); 1.278 + 1.279 + // This is intended to be nsHttpConnectionMgr:nsConnectionHandle taken 1.280 + // from the first transaction on this session. That object contains the 1.281 + // pointer to the real network-level nsHttpConnection object. 1.282 + nsRefPtr<nsAHttpConnection> mConnection; 1.283 + 1.284 + // The underlying socket transport object is needed to propogate some events 1.285 + nsISocketTransport *mSocketTransport; 1.286 + 1.287 + // These are temporary state variables to hold the argument to 1.288 + // Read/WriteSegments so it can be accessed by On(read/write)segment 1.289 + // further up the stack. 1.290 + nsAHttpSegmentReader *mSegmentReader; 1.291 + nsAHttpSegmentWriter *mSegmentWriter; 1.292 + 1.293 + uint32_t mSendingChunkSize; /* the transmission chunk size */ 1.294 + uint32_t mNextStreamID; /* 24 bits */ 1.295 + uint32_t mConcurrentHighWater; /* max parallelism on session */ 1.296 + uint32_t mPushAllowance; /* rwin for unmatched pushes */ 1.297 + 1.298 + internalStateType mDownstreamState; /* in frame, between frames, etc.. */ 1.299 + 1.300 + // Maintain 2 indexes - one by stream ID, one by transaction pointer. 1.301 + // There are also several lists of streams: ready to write, queued due to 1.302 + // max parallelism, streams that need to force a read for push, and the full 1.303 + // set of pushed streams. 1.304 + // The objects are not ref counted - they get destroyed 1.305 + // by the nsClassHashtable implementation when they are removed from 1.306 + // the transaction hash. 1.307 + nsDataHashtable<nsUint32HashKey, Http2Stream *> mStreamIDHash; 1.308 + nsClassHashtable<nsPtrHashKey<nsAHttpTransaction>, 1.309 + Http2Stream> mStreamTransactionHash; 1.310 + 1.311 + nsDeque mReadyForWrite; 1.312 + nsDeque mQueuedStreams; 1.313 + nsDeque mReadyForRead; 1.314 + nsTArray<Http2PushedStream *> mPushedStreams; 1.315 + 1.316 + // Compression contexts for header transport. 1.317 + // HTTP/2 compresses only HTTP headers and does not reset the context in between 1.318 + // frames. Even data that is not associated with a stream (e.g invalid 1.319 + // stream ID) is passed through these contexts to keep the compression 1.320 + // context correct. 1.321 + Http2Compressor mCompressor; 1.322 + Http2Decompressor mDecompressor; 1.323 + nsCString mDecompressBuffer; 1.324 + 1.325 + // mInputFrameBuffer is used to store received control packets and the 8 bytes 1.326 + // of header on data packets 1.327 + uint32_t mInputFrameBufferSize; // buffer allocation 1.328 + uint32_t mInputFrameBufferUsed; // amt of allocation used 1.329 + nsAutoArrayPtr<char> mInputFrameBuffer; 1.330 + 1.331 + // mInputFrameDataSize/Read are used for tracking the amount of data consumed 1.332 + // in a frame after the 8 byte header. Control frames are always fully buffered 1.333 + // and the fixed 8 byte leading header is at mInputFrameBuffer + 0, the first 1.334 + // data byte (i.e. the first settings/goaway/etc.. specific byte) is at 1.335 + // mInputFrameBuffer + 8 1.336 + // The frame size is mInputFrameDataSize + the constant 8 byte header 1.337 + uint32_t mInputFrameDataSize; 1.338 + uint32_t mInputFrameDataRead; 1.339 + bool mInputFrameFinal; // This frame was marked FIN 1.340 + uint8_t mInputFrameType; 1.341 + uint8_t mInputFrameFlags; 1.342 + uint32_t mInputFrameID; 1.343 + uint16_t mPaddingLength; 1.344 + 1.345 + // When a frame has been received that is addressed to a particular stream 1.346 + // (e.g. a data frame after the stream-id has been decoded), this points 1.347 + // to the stream. 1.348 + Http2Stream *mInputFrameDataStream; 1.349 + 1.350 + // mNeedsCleanup is a state variable to defer cleanup of a closed stream 1.351 + // If needed, It is set in session::OnWriteSegments() and acted on and 1.352 + // cleared when the stack returns to session::WriteSegments(). The stream 1.353 + // cannot be destroyed directly out of OnWriteSegments because 1.354 + // stream::writeSegments() is on the stack at that time. 1.355 + Http2Stream *mNeedsCleanup; 1.356 + 1.357 + // This reason code in the last processed RESET frame 1.358 + uint32_t mDownstreamRstReason; 1.359 + 1.360 + // When HEADERS/PROMISE are chained together, this is the expected ID of the next 1.361 + // recvd frame which must be the same type 1.362 + uint32_t mExpectedHeaderID; 1.363 + uint32_t mExpectedPushPromiseID; 1.364 + uint32_t mContinuedPromiseStream; 1.365 + 1.366 + // for the conversion of downstream http headers into http/2 formatted headers 1.367 + // The data here does not persist between frames 1.368 + nsCString mFlatHTTPResponseHeaders; 1.369 + uint32_t mFlatHTTPResponseHeadersOut; 1.370 + 1.371 + // when set, the session will go away when it reaches 0 streams. This flag 1.372 + // is set when: the stream IDs are running out (at either the client or the 1.373 + // server), when DontReuse() is called, a RST that is not specific to a 1.374 + // particular stream is received, a GOAWAY frame has been received from 1.375 + // the server. 1.376 + bool mShouldGoAway; 1.377 + 1.378 + // the session has received a nsAHttpTransaction::Close() call 1.379 + bool mClosed; 1.380 + 1.381 + // the session received a GoAway frame with a valid GoAwayID 1.382 + bool mCleanShutdown; 1.383 + 1.384 + // The TLS comlpiance checks are not done in the ctor beacuse of bad 1.385 + // exception handling - so we do them at IO time and cache the result 1.386 + bool mTLSProfileConfirmed; 1.387 + 1.388 + // A specifc reason code for the eventual GoAway frame. If set to NO_HTTP_ERROR 1.389 + // only NO_HTTP_ERROR, PROTOCOL_ERROR, or INTERNAL_ERROR will be sent. 1.390 + errorType mGoAwayReason; 1.391 + 1.392 + // If a GoAway message was received this is the ID of the last valid 1.393 + // stream. 0 otherwise. (0 is never a valid stream id.) 1.394 + uint32_t mGoAwayID; 1.395 + 1.396 + // The last stream processed ID we will send in our GoAway frame. 1.397 + uint32_t mOutgoingGoAwayID; 1.398 + 1.399 + // The limit on number of concurrent streams for this session. Normally it 1.400 + // is basically unlimited, but the SETTINGS control message from the 1.401 + // server might bring it down. 1.402 + uint32_t mMaxConcurrent; 1.403 + 1.404 + // The actual number of concurrent streams at this moment. Generally below 1.405 + // mMaxConcurrent, but the max can be lowered in real time to a value 1.406 + // below the current value 1.407 + uint32_t mConcurrent; 1.408 + 1.409 + // The number of server initiated promises, tracked for telemetry 1.410 + uint32_t mServerPushedResources; 1.411 + 1.412 + // The server rwin for new streams as determined from a SETTINGS frame 1.413 + uint32_t mServerInitialStreamWindow; 1.414 + 1.415 + // The Local Session window is how much data the server is allowed to send 1.416 + // (across all streams) without getting a window update to stream 0. It is 1.417 + // signed because asynchronous changes via SETTINGS can drive it negative. 1.418 + int64_t mLocalSessionWindow; 1.419 + 1.420 + // The Remote Session Window is how much data the client is allowed to send 1.421 + // (across all streams) without receiving a window update to stream 0. It is 1.422 + // signed because asynchronous changes via SETTINGS can drive it negative. 1.423 + int64_t mServerSessionWindow; 1.424 + 1.425 + // This is a output queue of bytes ready to be written to the SSL stream. 1.426 + // When that streams returns WOULD_BLOCK on direct write the bytes get 1.427 + // coalesced together here. This results in larger writes to the SSL layer. 1.428 + // The buffer is not dynamically grown to accomodate stream writes, but 1.429 + // does expand to accept infallible session wide frames like GoAway and RST. 1.430 + uint32_t mOutputQueueSize; 1.431 + uint32_t mOutputQueueUsed; 1.432 + uint32_t mOutputQueueSent; 1.433 + nsAutoArrayPtr<char> mOutputQueueBuffer; 1.434 + 1.435 + PRIntervalTime mPingThreshold; 1.436 + PRIntervalTime mLastReadEpoch; // used for ping timeouts 1.437 + PRIntervalTime mLastDataReadEpoch; // used for IdleTime() 1.438 + PRIntervalTime mPingSentEpoch; 1.439 + 1.440 + // used as a temporary buffer while enumerating the stream hash during GoAway 1.441 + nsDeque mGoAwayStreamsToRestart; 1.442 + 1.443 + // Each session gets a unique serial number because the push cache is correlated 1.444 + // by the load group and the serial number can be used as part of the cache key 1.445 + // to make sure streams aren't shared across sessions. 1.446 + uint64_t mSerial; 1.447 +}; 1.448 + 1.449 +} // namespace mozilla::net 1.450 +} // namespace mozilla 1.451 + 1.452 +#endif // mozilla_net_Http2Session_h