1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 :
3 : /* ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is Mozilla.
17 : *
18 : * The Initial Developer of the Original Code is
19 : * Mozilla Foundation.
20 : * Portions created by the Initial Developer are Copyright (C) 2011
21 : * the Initial Developer. All Rights Reserved.
22 : *
23 : * Contributor(s):
24 : * Patrick McManus <mcmanus@ducksong.com>
25 : *
26 : * Alternatively, the contents of this file may be used under the terms of
27 : * either of the GNU General Public License Version 2 or later (the "GPL"),
28 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 : * in which case the provisions of the GPL or the LGPL are applicable instead
30 : * of those above. If you wish to allow use of your version of this file only
31 : * under the terms of either the GPL or the LGPL, and not to allow others to
32 : * use your version of this file under the terms of the MPL, indicate your
33 : * decision by deleting the provisions above and replace them with the notice
34 : * and other provisions required by the GPL or the LGPL. If you do not delete
35 : * the provisions above, a recipient may use your version of this file under
36 : * the terms of any one of the MPL, the GPL or the LGPL.
37 : *
38 : * ***** END LICENSE BLOCK ***** */
39 :
40 : #ifndef mozilla_net_SpdySession_h
41 : #define mozilla_net_SpdySession_h
42 :
43 : // SPDY as defined by
44 : // http://www.chromium.org/spdy/spdy-protocol/spdy-protocol-draft2
45 :
46 : #include "nsAHttpTransaction.h"
47 : #include "nsAHttpConnection.h"
48 : #include "nsClassHashtable.h"
49 : #include "nsDataHashtable.h"
50 : #include "nsDeque.h"
51 : #include "nsHashKeys.h"
52 : #include "zlib.h"
53 :
54 : class nsHttpConnection;
55 : class nsISocketTransport;
56 :
57 : namespace mozilla { namespace net {
58 :
59 : class SpdyStream;
60 :
61 : class SpdySession : public nsAHttpTransaction
62 : , public nsAHttpConnection
63 : , public nsAHttpSegmentReader
64 : , public nsAHttpSegmentWriter
65 : {
66 : public:
67 : NS_DECL_ISUPPORTS
68 : NS_DECL_NSAHTTPTRANSACTION
69 : NS_DECL_NSAHTTPCONNECTION
70 : NS_DECL_NSAHTTPSEGMENTREADER
71 : NS_DECL_NSAHTTPSEGMENTWRITER
72 :
73 : SpdySession(nsAHttpTransaction *, nsISocketTransport *, PRInt32);
74 : ~SpdySession();
75 :
76 : bool AddStream(nsAHttpTransaction *, PRInt32);
77 0 : bool CanReuse() { return !mShouldGoAway && !mClosed; }
78 : void DontReuse();
79 : bool RoomForMoreStreams();
80 :
81 : // When the connection is active this is called every 15 seconds
82 : void ReadTimeoutTick(PRIntervalTime now);
83 :
84 : // Idle time represents time since "goodput".. e.g. a data or header frame
85 : PRIntervalTime IdleTime();
86 :
87 : PRUint32 RegisterStreamID(SpdyStream *);
88 :
89 : const static PRUint8 kFlag_Control = 0x80;
90 :
91 : const static PRUint8 kFlag_Data_FIN = 0x01;
92 : const static PRUint8 kFlag_Data_UNI = 0x02;
93 : const static PRUint8 kFlag_Data_ZLIB = 0x02;
94 :
95 : // The protocol document for v2 specifies that the
96 : // highest value (3) is the highest priority, but in
97 : // reality 0 is the highest priority.
98 : //
99 : // Draft 3 notes here https://sites.google.com/a/chromium.org/dev/spdy/spdy-protocol/
100 : // are the best guide to the mistake. Also see
101 : // GetLowestPriority() and GetHighestPriority() in spdy_framer.h of
102 : // chromium source.
103 :
104 : const static PRUint8 kPri00 = 0 << 6; // highest
105 : const static PRUint8 kPri01 = 1 << 6;
106 : const static PRUint8 kPri02 = 2 << 6;
107 : const static PRUint8 kPri03 = 3 << 6; // lowest
108 :
109 : enum
110 : {
111 : CONTROL_TYPE_FIRST = 0,
112 : CONTROL_TYPE_SYN_STREAM = 1,
113 : CONTROL_TYPE_SYN_REPLY = 2,
114 : CONTROL_TYPE_RST_STREAM = 3,
115 : CONTROL_TYPE_SETTINGS = 4,
116 : CONTROL_TYPE_NOOP = 5,
117 : CONTROL_TYPE_PING = 6,
118 : CONTROL_TYPE_GOAWAY = 7,
119 : CONTROL_TYPE_HEADERS = 8,
120 : CONTROL_TYPE_WINDOW_UPDATE = 9, /* no longer in v2 */
121 : CONTROL_TYPE_LAST = 10
122 : };
123 :
124 : enum rstReason
125 : {
126 : RST_PROTOCOL_ERROR = 1,
127 : RST_INVALID_STREAM = 2,
128 : RST_REFUSED_STREAM = 3,
129 : RST_UNSUPPORTED_VERSION = 4,
130 : RST_CANCEL = 5,
131 : RST_INTERNAL_ERROR = 6,
132 : RST_FLOW_CONTROL_ERROR = 7,
133 : RST_BAD_ASSOC_STREAM = 8
134 : };
135 :
136 : enum
137 : {
138 : SETTINGS_TYPE_UPLOAD_BW = 1, // kb/s
139 : SETTINGS_TYPE_DOWNLOAD_BW = 2, // kb/s
140 : SETTINGS_TYPE_RTT = 3, // ms
141 : SETTINGS_TYPE_MAX_CONCURRENT = 4, // streams
142 : SETTINGS_TYPE_CWND = 5, // packets
143 : SETTINGS_TYPE_DOWNLOAD_RETRANS_RATE = 6, // percentage
144 : SETTINGS_TYPE_INITIAL_WINDOW = 7 // bytes. Not used in v2.
145 : };
146 :
147 : // This should be big enough to hold all of your control packets,
148 : // but if it needs to grow for huge headers it can do so dynamically.
149 : // About 1% of requests to SPDY google services seem to be > 1000
150 : // with all less than 2000.
151 : const static PRUint32 kDefaultBufferSize = 2048;
152 :
153 : // kDefaultQueueSize must be >= other queue size constants
154 : const static PRUint32 kDefaultQueueSize = 16384;
155 : const static PRUint32 kQueueMinimumCleanup = 8192;
156 : const static PRUint32 kQueueTailRoom = 4096;
157 : const static PRUint32 kQueueReserved = 1024;
158 :
159 : const static PRUint32 kSendingChunkSize = 4096;
160 : const static PRUint32 kDefaultMaxConcurrent = 100;
161 : const static PRUint32 kMaxStreamID = 0x7800000;
162 :
163 : static nsresult HandleSynStream(SpdySession *);
164 : static nsresult HandleSynReply(SpdySession *);
165 : static nsresult HandleRstStream(SpdySession *);
166 : static nsresult HandleSettings(SpdySession *);
167 : static nsresult HandleNoop(SpdySession *);
168 : static nsresult HandlePing(SpdySession *);
169 : static nsresult HandleGoAway(SpdySession *);
170 : static nsresult HandleHeaders(SpdySession *);
171 : static nsresult HandleWindowUpdate(SpdySession *);
172 :
173 : static void EnsureBuffer(nsAutoArrayPtr<char> &,
174 : PRUint32, PRUint32, PRUint32 &);
175 :
176 : // For writing the SPDY data stream to LOG4
177 : static void LogIO(SpdySession *, SpdyStream *, const char *,
178 : const char *, PRUint32);
179 :
180 : // an overload of nsAHttpConnection
181 : void TransactionHasDataToWrite(nsAHttpTransaction *);
182 :
183 : // a similar version for SpdyStream
184 : void TransactionHasDataToWrite(SpdyStream *);
185 :
186 : // an overload of nsAHttpSegementReader
187 : virtual nsresult CommitToSegmentSize(PRUint32 size);
188 :
189 : private:
190 :
191 : enum stateType {
192 : BUFFERING_FRAME_HEADER,
193 : BUFFERING_CONTROL_FRAME,
194 : PROCESSING_DATA_FRAME,
195 : DISCARDING_DATA_FRAME,
196 : PROCESSING_CONTROL_SYN_REPLY,
197 : PROCESSING_CONTROL_RST_STREAM
198 : };
199 :
200 : void DeterminePingThreshold();
201 : nsresult HandleSynReplyForValidStream();
202 : PRUint32 GetWriteQueueSize();
203 : void ChangeDownstreamState(enum stateType);
204 : void ResetDownstreamState();
205 : nsresult DownstreamUncompress(char *, PRUint32);
206 : void zlibInit();
207 : nsresult FindHeader(nsCString, nsDependentCSubstring &);
208 : nsresult ConvertHeaders(nsDependentCSubstring &,
209 : nsDependentCSubstring &);
210 : void GeneratePing(PRUint32);
211 : void ClearPing(bool);
212 : void GenerateRstStream(PRUint32, PRUint32);
213 : void GenerateGoAway();
214 : void CleanupStream(SpdyStream *, nsresult, rstReason);
215 :
216 : void SetWriteCallbacks();
217 : void FlushOutputQueue();
218 :
219 : bool RoomForMoreConcurrent();
220 : void ActivateStream(SpdyStream *);
221 : void ProcessPending();
222 :
223 : // a wrapper for all calls to the nshttpconnection level segment writer. Used
224 : // to track network I/O for timeout purposes
225 : nsresult NetworkRead(nsAHttpSegmentWriter *, char *, PRUint32, PRUint32 *);
226 :
227 : static PLDHashOperator ShutdownEnumerator(nsAHttpTransaction *,
228 : nsAutoPtr<SpdyStream> &,
229 : void *);
230 :
231 : // This is intended to be nsHttpConnectionMgr:nsHttpConnectionHandle taken
232 : // from the first transaction on this session. That object contains the
233 : // pointer to the real network-level nsHttpConnection object.
234 : nsRefPtr<nsAHttpConnection> mConnection;
235 :
236 : // The underlying socket transport object is needed to propogate some events
237 : nsISocketTransport *mSocketTransport;
238 :
239 : // These are temporary state variables to hold the argument to
240 : // Read/WriteSegments so it can be accessed by On(read/write)segment
241 : // further up the stack.
242 : nsAHttpSegmentReader *mSegmentReader;
243 : nsAHttpSegmentWriter *mSegmentWriter;
244 :
245 : PRUint32 mSendingChunkSize; /* the transmission chunk size */
246 : PRUint32 mNextStreamID; /* 24 bits */
247 : PRUint32 mConcurrentHighWater; /* max parallelism on session */
248 :
249 : stateType mDownstreamState; /* in frame, between frames, etc.. */
250 :
251 : // Maintain 5 indexes - one by stream ID, one by transaction ptr,
252 : // one list of streams ready to write, one list of streams that are queued
253 : // due to max parallelism settings, and one list of streams
254 : // that must be given priority to write for window updates. The objects
255 : // are not ref counted - they get destroyed
256 : // by the nsClassHashtable implementation when they are removed from
257 : // there.
258 : nsDataHashtable<nsUint32HashKey, SpdyStream *> mStreamIDHash;
259 : nsClassHashtable<nsPtrHashKey<nsAHttpTransaction>,
260 : SpdyStream> mStreamTransactionHash;
261 : nsDeque mReadyForWrite;
262 : nsDeque mQueuedStreams;
263 :
264 : // UrgentForWrite is meant to carry window updates. They were defined in
265 : // the v2 spec but apparently never implemented so are now scheduled to
266 : // be removed. But they will be reintroduced for v3, so we will leave
267 : // this queue in place to ease that transition.
268 : nsDeque mUrgentForWrite;
269 :
270 : // Compression contexts for header transport using deflate.
271 : // SPDY compresses only HTTP headers and does not reset zlib in between
272 : // frames.
273 : z_stream mDownstreamZlib;
274 : z_stream mUpstreamZlib;
275 :
276 : // mInputFrameBuffer is used to store received control packets and the 8 bytes
277 : // of header on data packets
278 : PRUint32 mInputFrameBufferSize;
279 : PRUint32 mInputFrameBufferUsed;
280 : nsAutoArrayPtr<char> mInputFrameBuffer;
281 :
282 : // mInputFrameDataSize/Read are used for tracking the amount of data consumed
283 : // in a data frame. the data itself is not buffered in spdy
284 : // The frame size is mInputFrameDataSize + the constant 8 byte header
285 : PRUint32 mInputFrameDataSize;
286 : PRUint32 mInputFrameDataRead;
287 : bool mInputFrameDataLast; // This frame was marked FIN
288 :
289 : // When a frame has been received that is addressed to a particular stream
290 : // (e.g. a data frame after the stream-id has been decoded), this points
291 : // to the stream.
292 : SpdyStream *mInputFrameDataStream;
293 :
294 : // mNeedsCleanup is a state variable to defer cleanup of a closed stream
295 : // If needed, It is set in session::OnWriteSegments() and acted on and
296 : // cleared when the stack returns to session::WriteSegments(). The stream
297 : // cannot be destroyed directly out of OnWriteSegments because
298 : // stream::writeSegments() is on the stack at that time.
299 : SpdyStream *mNeedsCleanup;
300 :
301 : // The CONTROL_TYPE value for a control frame
302 : PRUint32 mFrameControlType;
303 :
304 : // This reason code in the last processed RESET frame
305 : PRUint32 mDownstreamRstReason;
306 :
307 : // These are used for decompressing downstream spdy response headers
308 : // This is done at the session level because sometimes the stream
309 : // has already been canceled but the decompression still must happen
310 : // to keep the zlib state correct for the next state of headers.
311 : PRUint32 mDecompressBufferSize;
312 : PRUint32 mDecompressBufferUsed;
313 : nsAutoArrayPtr<char> mDecompressBuffer;
314 :
315 : // for the conversion of downstream http headers into spdy formatted headers
316 : nsCString mFlatHTTPResponseHeaders;
317 : PRUint32 mFlatHTTPResponseHeadersOut;
318 :
319 : // when set, the session will go away when it reaches 0 streams. This flag
320 : // is set when: the stream IDs are running out (at either the client or the
321 : // server), when DontReuse() is called, a RST that is not specific to a
322 : // particular stream is received, a GOAWAY frame has been received from
323 : // the server.
324 : bool mShouldGoAway;
325 :
326 : // the session has received a nsAHttpTransaction::Close() call
327 : bool mClosed;
328 :
329 : // the session received a GoAway frame with a valid GoAwayID
330 : bool mCleanShutdown;
331 :
332 : // If a GoAway message was received this is the ID of the last valid
333 : // stream. 0 otherwise. (0 is never a valid stream id.)
334 : PRUint32 mGoAwayID;
335 :
336 : // The limit on number of concurrent streams for this session. Normally it
337 : // is basically unlimited, but the SETTINGS control message from the
338 : // server might bring it down.
339 : PRUint32 mMaxConcurrent;
340 :
341 : // The actual number of concurrent streams at this moment. Generally below
342 : // mMaxConcurrent, but the max can be lowered in real time to a value
343 : // below the current value
344 : PRUint32 mConcurrent;
345 :
346 : // The number of server initiated SYN-STREAMS, tracked for telemetry
347 : PRUint32 mServerPushedResources;
348 :
349 : // This is a output queue of bytes ready to be written to the SSL stream.
350 : // When that streams returns WOULD_BLOCK on direct write the bytes get
351 : // coalesced together here. This results in larger writes to the SSL layer.
352 : // The buffer is not dynamically grown to accomodate stream writes, but
353 : // does expand to accept infallible session wide frames like GoAway and RST.
354 : PRUint32 mOutputQueueSize;
355 : PRUint32 mOutputQueueUsed;
356 : PRUint32 mOutputQueueSent;
357 : nsAutoArrayPtr<char> mOutputQueueBuffer;
358 :
359 : PRIntervalTime mPingThreshold;
360 : PRIntervalTime mLastReadEpoch; // used for ping timeouts
361 : PRIntervalTime mLastDataReadEpoch; // used for IdleTime()
362 : PRIntervalTime mPingSentEpoch;
363 : PRUint32 mNextPingID;
364 : bool mPingThresholdExperiment;
365 : };
366 :
367 : }} // namespace mozilla::net
368 :
369 : #endif // mozilla_net_SpdySession_h
|