netwerk/protocol/http/SpdySession3.h

branch
TOR_BUG_9701
changeset 11
deefc01c0e14
equal deleted inserted replaced
-1:000000000000 0:f4f41add3819
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_SpdySession3_h
7 #define mozilla_net_SpdySession3_h
8
9 // SPDY as defined by
10 // http://dev.chromium.org/spdy/spdy-protocol/spdy-protocol-draft3
11
12 #include "ASpdySession.h"
13 #include "mozilla/Attributes.h"
14 #include "nsAHttpConnection.h"
15 #include "nsClassHashtable.h"
16 #include "nsDataHashtable.h"
17 #include "nsDeque.h"
18 #include "nsHashKeys.h"
19 #include "zlib.h"
20
21 class nsISocketTransport;
22
23 namespace mozilla { namespace net {
24
25 class SpdyPushedStream3;
26 class SpdyStream3;
27
28 class SpdySession3 MOZ_FINAL : public ASpdySession
29 , public nsAHttpConnection
30 , public nsAHttpSegmentReader
31 , public nsAHttpSegmentWriter
32 {
33 public:
34 NS_DECL_THREADSAFE_ISUPPORTS
35 NS_DECL_NSAHTTPTRANSACTION
36 NS_DECL_NSAHTTPCONNECTION(mConnection)
37 NS_DECL_NSAHTTPSEGMENTREADER
38 NS_DECL_NSAHTTPSEGMENTWRITER
39
40 SpdySession3(nsAHttpTransaction *, nsISocketTransport *, int32_t);
41 ~SpdySession3();
42
43 bool AddStream(nsAHttpTransaction *, int32_t);
44 bool CanReuse() { return !mShouldGoAway && !mClosed; }
45 bool RoomForMoreStreams();
46
47 // When the connection is active this is called up to once every 1 second
48 // return the interval (in seconds) that the connection next wants to
49 // have this invoked. It might happen sooner depending on the needs of
50 // other connections.
51 uint32_t ReadTimeoutTick(PRIntervalTime now);
52
53 // Idle time represents time since "goodput".. e.g. a data or header frame
54 PRIntervalTime IdleTime();
55
56 // Registering with a newID of 0 means pick the next available odd ID
57 uint32_t RegisterStreamID(SpdyStream3 *, uint32_t aNewID = 0);
58
59 const static uint8_t kVersion = 3;
60
61 const static uint8_t kFlag_Control = 0x80;
62
63 const static uint8_t kFlag_Data_FIN = 0x01;
64 const static uint8_t kFlag_Data_UNI = 0x02;
65
66 enum
67 {
68 CONTROL_TYPE_FIRST = 0,
69 CONTROL_TYPE_SYN_STREAM = 1,
70 CONTROL_TYPE_SYN_REPLY = 2,
71 CONTROL_TYPE_RST_STREAM = 3,
72 CONTROL_TYPE_SETTINGS = 4,
73 CONTROL_TYPE_NOOP = 5, /* deprecated */
74 CONTROL_TYPE_PING = 6,
75 CONTROL_TYPE_GOAWAY = 7,
76 CONTROL_TYPE_HEADERS = 8,
77 CONTROL_TYPE_WINDOW_UPDATE = 9,
78 CONTROL_TYPE_CREDENTIAL = 10,
79 CONTROL_TYPE_LAST = 11
80 };
81
82 enum rstReason
83 {
84 RST_PROTOCOL_ERROR = 1,
85 RST_INVALID_STREAM = 2,
86 RST_REFUSED_STREAM = 3,
87 RST_UNSUPPORTED_VERSION = 4,
88 RST_CANCEL = 5,
89 RST_INTERNAL_ERROR = 6,
90 RST_FLOW_CONTROL_ERROR = 7,
91 RST_STREAM_IN_USE = 8,
92 RST_STREAM_ALREADY_CLOSED = 9,
93 RST_INVALID_CREDENTIALS = 10,
94 RST_FRAME_TOO_LARGE = 11
95 };
96
97 enum goawayReason
98 {
99 OK = 0,
100 PROTOCOL_ERROR = 1,
101 INTERNAL_ERROR = 2, // sometimes misdocumented as 11
102 NUM_STATUS_CODES = 3 // reserved by chromium but undocumented
103 };
104
105 enum settingsFlags
106 {
107 PERSIST_VALUE = 1,
108 PERSISTED_VALUE = 2
109 };
110
111 enum
112 {
113 SETTINGS_TYPE_UPLOAD_BW = 1, // kb/s
114 SETTINGS_TYPE_DOWNLOAD_BW = 2, // kb/s
115 SETTINGS_TYPE_RTT = 3, // ms
116 SETTINGS_TYPE_MAX_CONCURRENT = 4, // streams
117 SETTINGS_TYPE_CWND = 5, // packets
118 SETTINGS_TYPE_DOWNLOAD_RETRANS_RATE = 6, // percentage
119 SETTINGS_TYPE_INITIAL_WINDOW = 7, // bytes for flow control
120 SETTINGS_CLIENT_CERTIFICATE_VECTOR_SIZE = 8
121 };
122
123 // This should be big enough to hold all of your control packets,
124 // but if it needs to grow for huge headers it can do so dynamically.
125 // About 1% of responses from SPDY google services seem to be > 1000
126 // with all less than 2000 when compression is enabled.
127 const static uint32_t kDefaultBufferSize = 2048;
128
129 // kDefaultQueueSize must be >= other queue size constants
130 const static uint32_t kDefaultQueueSize = 32768;
131 const static uint32_t kQueueMinimumCleanup = 24576;
132 const static uint32_t kQueueTailRoom = 4096;
133 const static uint32_t kQueueReserved = 1024;
134
135 const static uint32_t kDefaultMaxConcurrent = 100;
136 const static uint32_t kMaxStreamID = 0x7800000;
137
138 // This is a sentinel for a deleted stream. It is not a valid
139 // 31 bit stream ID.
140 const static uint32_t kDeadStreamID = 0xffffdead;
141
142 // below the emergency threshold of local window we ack every received
143 // byte. Above that we coalesce bytes into the MinimumToAck size.
144 const static int32_t kEmergencyWindowThreshold = 1024 * 1024;
145 const static uint32_t kMinimumToAck = 64 * 1024;
146
147 // The default peer rwin is 64KB unless updated by a settings frame
148 const static uint32_t kDefaultServerRwin = 64 * 1024;
149
150 static nsresult HandleSynStream(SpdySession3 *);
151 static nsresult HandleSynReply(SpdySession3 *);
152 static nsresult HandleRstStream(SpdySession3 *);
153 static nsresult HandleSettings(SpdySession3 *);
154 static nsresult HandleNoop(SpdySession3 *);
155 static nsresult HandlePing(SpdySession3 *);
156 static nsresult HandleGoAway(SpdySession3 *);
157 static nsresult HandleHeaders(SpdySession3 *);
158 static nsresult HandleWindowUpdate(SpdySession3 *);
159 static nsresult HandleCredential(SpdySession3 *);
160
161 template<typename T>
162 static void EnsureBuffer(nsAutoArrayPtr<T> &,
163 uint32_t, uint32_t, uint32_t &);
164
165 // For writing the SPDY data stream to LOG4
166 static void LogIO(SpdySession3 *, SpdyStream3 *, const char *,
167 const char *, uint32_t);
168
169 // an overload of nsAHttpConnection
170 void TransactionHasDataToWrite(nsAHttpTransaction *);
171
172 // a similar version for SpdyStream3
173 void TransactionHasDataToWrite(SpdyStream3 *);
174
175 // an overload of nsAHttpSegementReader
176 virtual nsresult CommitToSegmentSize(uint32_t size, bool forceCommitment);
177
178 uint32_t GetServerInitialWindow() { return mServerInitialWindow; }
179
180 void ConnectPushedStream(SpdyStream3 *stream);
181 void DecrementConcurrent(SpdyStream3 *stream);
182
183 uint64_t Serial() { return mSerial; }
184
185 void PrintDiagnostics (nsCString &log);
186
187 // Streams need access to these
188 uint32_t SendingChunkSize() { return mSendingChunkSize; }
189 uint32_t PushAllowance() { return mPushAllowance; }
190 z_stream *UpstreamZlib() { return &mUpstreamZlib; }
191 nsISocketTransport *SocketTransport() { return mSocketTransport; }
192
193 private:
194
195 enum stateType {
196 BUFFERING_FRAME_HEADER,
197 BUFFERING_CONTROL_FRAME,
198 PROCESSING_DATA_FRAME,
199 DISCARDING_DATA_FRAME,
200 PROCESSING_COMPLETE_HEADERS,
201 PROCESSING_CONTROL_RST_STREAM
202 };
203
204 nsresult ResponseHeadersComplete();
205 uint32_t GetWriteQueueSize();
206 void ChangeDownstreamState(enum stateType);
207 void ResetDownstreamState();
208 nsresult UncompressAndDiscard(uint32_t, uint32_t);
209 void zlibInit();
210 void GeneratePing(uint32_t);
211 void GenerateRstStream(uint32_t, uint32_t);
212 void GenerateGoAway(uint32_t);
213 void CleanupStream(SpdyStream3 *, nsresult, rstReason);
214 void CloseStream(SpdyStream3 *, nsresult);
215 void GenerateSettings();
216 void RemoveStreamFromQueues(SpdyStream3 *);
217
218 void SetWriteCallbacks();
219 void FlushOutputQueue();
220 void RealignOutputQueue();
221
222 bool RoomForMoreConcurrent();
223 void ActivateStream(SpdyStream3 *);
224 void ProcessPending();
225 nsresult SetInputFrameDataStream(uint32_t);
226 bool VerifyStream(SpdyStream3 *, uint32_t);
227 void SetNeedsCleanup();
228
229 void UpdateLocalRwin(SpdyStream3 *stream, uint32_t bytes);
230
231 // a wrapper for all calls to the nshttpconnection level segment writer. Used
232 // to track network I/O for timeout purposes
233 nsresult NetworkRead(nsAHttpSegmentWriter *, char *, uint32_t, uint32_t *);
234
235 static PLDHashOperator ShutdownEnumerator(nsAHttpTransaction *,
236 nsAutoPtr<SpdyStream3> &,
237 void *);
238
239 static PLDHashOperator GoAwayEnumerator(nsAHttpTransaction *,
240 nsAutoPtr<SpdyStream3> &,
241 void *);
242
243 static PLDHashOperator UpdateServerRwinEnumerator(nsAHttpTransaction *,
244 nsAutoPtr<SpdyStream3> &,
245 void *);
246
247 // This is intended to be nsHttpConnectionMgr:nsConnectionHandle taken
248 // from the first transaction on this session. That object contains the
249 // pointer to the real network-level nsHttpConnection object.
250 nsRefPtr<nsAHttpConnection> mConnection;
251
252 // The underlying socket transport object is needed to propogate some events
253 nsISocketTransport *mSocketTransport;
254
255 // These are temporary state variables to hold the argument to
256 // Read/WriteSegments so it can be accessed by On(read/write)segment
257 // further up the stack.
258 nsAHttpSegmentReader *mSegmentReader;
259 nsAHttpSegmentWriter *mSegmentWriter;
260
261 uint32_t mSendingChunkSize; /* the transmission chunk size */
262 uint32_t mNextStreamID; /* 24 bits */
263 uint32_t mConcurrentHighWater; /* max parallelism on session */
264 uint32_t mPushAllowance; /* rwin for unmatched pushes */
265
266 stateType mDownstreamState; /* in frame, between frames, etc.. */
267
268 // Maintain 2 indexes - one by stream ID, one by transaction pointer.
269 // There are also several lists of streams: ready to write, queued due to
270 // max parallelism, streams that need to force a read for push, and the full
271 // set of pushed streams.
272 // The objects are not ref counted - they get destroyed
273 // by the nsClassHashtable implementation when they are removed from
274 // the transaction hash.
275 nsDataHashtable<nsUint32HashKey, SpdyStream3 *> mStreamIDHash;
276 nsClassHashtable<nsPtrHashKey<nsAHttpTransaction>,
277 SpdyStream3> mStreamTransactionHash;
278
279 nsDeque mReadyForWrite;
280 nsDeque mQueuedStreams;
281 nsDeque mReadyForRead;
282 nsTArray<SpdyPushedStream3 *> mPushedStreams;
283
284 // Compression contexts for header transport using deflate.
285 // SPDY compresses only HTTP headers and does not reset zlib in between
286 // frames. Even data that is not associated with a stream (e.g invalid
287 // stream ID) is passed through these contexts to keep the compression
288 // context correct.
289 z_stream mDownstreamZlib;
290 z_stream mUpstreamZlib;
291
292 // mInputFrameBuffer is used to store received control packets and the 8 bytes
293 // of header on data packets
294 uint32_t mInputFrameBufferSize;
295 uint32_t mInputFrameBufferUsed;
296 nsAutoArrayPtr<char> mInputFrameBuffer;
297
298 // mInputFrameDataSize/Read are used for tracking the amount of data consumed
299 // in a data frame. the data itself is not buffered in spdy
300 // The frame size is mInputFrameDataSize + the constant 8 byte header
301 uint32_t mInputFrameDataSize;
302 uint32_t mInputFrameDataRead;
303 bool mInputFrameDataLast; // This frame was marked FIN
304
305 // When a frame has been received that is addressed to a particular stream
306 // (e.g. a data frame after the stream-id has been decoded), this points
307 // to the stream.
308 SpdyStream3 *mInputFrameDataStream;
309
310 // mNeedsCleanup is a state variable to defer cleanup of a closed stream
311 // If needed, It is set in session::OnWriteSegments() and acted on and
312 // cleared when the stack returns to session::WriteSegments(). The stream
313 // cannot be destroyed directly out of OnWriteSegments because
314 // stream::writeSegments() is on the stack at that time.
315 SpdyStream3 *mNeedsCleanup;
316
317 // The CONTROL_TYPE value for a control frame
318 uint32_t mFrameControlType;
319
320 // This reason code in the last processed RESET frame
321 uint32_t mDownstreamRstReason;
322
323 // for the conversion of downstream http headers into spdy formatted headers
324 // The data here does not persist between frames
325 nsCString mFlatHTTPResponseHeaders;
326 uint32_t mFlatHTTPResponseHeadersOut;
327
328 // when set, the session will go away when it reaches 0 streams. This flag
329 // is set when: the stream IDs are running out (at either the client or the
330 // server), when DontReuse() is called, a RST that is not specific to a
331 // particular stream is received, a GOAWAY frame has been received from
332 // the server.
333 bool mShouldGoAway;
334
335 // the session has received a nsAHttpTransaction::Close() call
336 bool mClosed;
337
338 // the session received a GoAway frame with a valid GoAwayID
339 bool mCleanShutdown;
340
341 // indicates PROCESSING_COMPLETE_HEADERS state was pushed onto the stack
342 // over an active PROCESSING_DATA_FRAME, which should be restored when
343 // the processed headers are written to the stream
344 bool mDataPending;
345
346 // If a GoAway message was received this is the ID of the last valid
347 // stream. 0 otherwise. (0 is never a valid stream id.)
348 uint32_t mGoAwayID;
349
350 // The limit on number of concurrent streams for this session. Normally it
351 // is basically unlimited, but the SETTINGS control message from the
352 // server might bring it down.
353 uint32_t mMaxConcurrent;
354
355 // The actual number of concurrent streams at this moment. Generally below
356 // mMaxConcurrent, but the max can be lowered in real time to a value
357 // below the current value
358 uint32_t mConcurrent;
359
360 // The number of server initiated SYN-STREAMS, tracked for telemetry
361 uint32_t mServerPushedResources;
362
363 // The server rwin for new streams as determined from a SETTINGS frame
364 uint32_t mServerInitialWindow;
365
366 // This is a output queue of bytes ready to be written to the SSL stream.
367 // When that streams returns WOULD_BLOCK on direct write the bytes get
368 // coalesced together here. This results in larger writes to the SSL layer.
369 // The buffer is not dynamically grown to accomodate stream writes, but
370 // does expand to accept infallible session wide frames like GoAway and RST.
371 uint32_t mOutputQueueSize;
372 uint32_t mOutputQueueUsed;
373 uint32_t mOutputQueueSent;
374 nsAutoArrayPtr<char> mOutputQueueBuffer;
375
376 PRIntervalTime mPingThreshold;
377 PRIntervalTime mLastReadEpoch; // used for ping timeouts
378 PRIntervalTime mLastDataReadEpoch; // used for IdleTime()
379 PRIntervalTime mPingSentEpoch;
380 uint32_t mNextPingID;
381
382 // used as a temporary buffer while enumerating the stream hash during GoAway
383 nsDeque mGoAwayStreamsToRestart;
384
385 // Each session gets a unique serial number because the push cache is correlated
386 // by the load group and the serial number can be used as part of the cache key
387 // to make sure streams aren't shared across sessions.
388 uint64_t mSerial;
389 };
390
391 }} // namespace mozilla::net
392
393 #endif // mozilla_net_SpdySession3_h

mercurial