summaryrefslogtreecommitdiffstats
path: root/net/spdy/spdy_framer.h
blob: 332a47f93452cf42b56be41419bbb29db637a898 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef NET_SPDY_SPDY_FRAMER_H_
#define NET_SPDY_SPDY_FRAMER_H_
#pragma once

#include <list>
#include <map>
#include <string>
#include <utility>

#include "base/basictypes.h"
#include "base/gtest_prod_util.h"
#include "base/memory/scoped_ptr.h"
#include "net/base/sys_byteorder.h"
#include "net/spdy/spdy_protocol.h"

typedef struct z_stream_s z_stream;  // Forward declaration for zlib.

namespace net {
class HttpProxyClientSocketPoolTest;
class HttpNetworkLayer;
class HttpNetworkTransactionTest;
class SpdyHttpStreamTest;
class SpdyNetworkTransactionTest;
class SpdyProxyClientSocketTest;
class SpdySessionTest;
class SpdyStreamTest;
}

namespace spdy {

class SpdyFramer;
class SpdyFramerTest;

namespace test {
class TestSpdyVisitor;
void FramerSetEnableCompressionHelper(SpdyFramer* framer, bool compress);
}  // namespace test

// A datastructure for holding a set of headers from either a
// SYN_STREAM or SYN_REPLY frame.
typedef std::map<std::string, std::string> SpdyHeaderBlock;

// A datastructure for holding a set of ID/value pairs for a SETTINGS frame.
typedef std::pair<spdy::SettingsFlagsAndId, uint32> SpdySetting;
typedef std::list<SpdySetting> SpdySettings;

// SpdyFramerVisitorInterface is a set of callbacks for the SpdyFramer.
// Implement this interface to receive event callbacks as frames are
// decoded from the framer.
class SpdyFramerVisitorInterface {
 public:
  virtual ~SpdyFramerVisitorInterface() {}

  // Called if an error is detected in the SpdyFrame protocol.
  virtual void OnError(SpdyFramer* framer) = 0;

  // Called when a Control Frame is received.
  virtual void OnControl(const SpdyControlFrame* frame) = 0;

  // Called when data is received.
  // |stream_id| The stream receiving data.
  // |data| A buffer containing the data received.
  // |len| The length of the data buffer.
  // When the other side has finished sending data on this stream,
  // this method will be called with a zero-length buffer.
  virtual void OnStreamFrameData(SpdyStreamId stream_id,
                                 const char* data,
                                 size_t len) = 0;
};

class SpdyFramer {
 public:
  // SPDY states.
  // TODO(mbelshe): Can we move these into the implementation
  //                and avoid exposing through the header.  (Needed for test)
  enum SpdyState {
    SPDY_ERROR,
    SPDY_DONE,
    SPDY_RESET,
    SPDY_AUTO_RESET,
    SPDY_READING_COMMON_HEADER,
    SPDY_INTERPRET_CONTROL_FRAME_COMMON_HEADER,
    SPDY_CONTROL_FRAME_PAYLOAD,
    SPDY_IGNORE_REMAINING_PAYLOAD,
    SPDY_FORWARD_STREAM_FRAME
  };

  // SPDY error codes.
  enum SpdyError {
    SPDY_NO_ERROR,
    SPDY_INVALID_CONTROL_FRAME,      // Control frame is mal-formatted.
    SPDY_CONTROL_PAYLOAD_TOO_LARGE,  // Control frame payload was too large.
    SPDY_ZLIB_INIT_FAILURE,          // The Zlib library could not initialize.
    SPDY_UNSUPPORTED_VERSION,        // Control frame has unsupported version.
    SPDY_DECOMPRESS_FAILURE,         // There was an error decompressing.
    SPDY_COMPRESS_FAILURE,           // There was an error compressing.

    LAST_ERROR,  // Must be the last entry in the enum.
  };

  // Create a new Framer.
  SpdyFramer();
  virtual ~SpdyFramer();

  // Set callbacks to be called from the framer.  A visitor must be set, or
  // else the framer will likely crash.  It is acceptable for the visitor
  // to do nothing.  If this is called multiple times, only the last visitor
  // will be used.
  void set_visitor(SpdyFramerVisitorInterface* visitor) {
    visitor_ = visitor;
  }

  // Pass data into the framer for parsing.
  // Returns the number of bytes consumed. It is safe to pass more bytes in
  // than may be consumed.
  size_t ProcessInput(const char* data, size_t len);

  // Resets the framer state after a frame has been successfully decoded.
  // TODO(mbelshe): can we make this private?
  void Reset();

  // Check the state of the framer.
  SpdyError error_code() const { return error_code_; }
  SpdyState state() const { return state_; }

  bool MessageFullyRead() {
    return state_ == SPDY_DONE || state_ == SPDY_AUTO_RESET;
  }
  bool HasError() { return state_ == SPDY_ERROR; }

  // Further parsing utilities.
  // Given a control frame, parse out a SpdyHeaderBlock.  Only
  // valid for SYN_STREAM and SYN_REPLY frames.
  // Returns true if successfully parsed, false otherwise.
  bool ParseHeaderBlock(const SpdyFrame* frame, SpdyHeaderBlock* block);

  // Create a SpdySynStreamControlFrame.
  // |stream_id| is the id for this stream.
  // |associated_stream_id| is the associated stream id for this stream.
  // |priority| is the priority (0-3) for this stream.
  // |flags| is the flags to use with the data.
  //    To mark this frame as the last frame, enable CONTROL_FLAG_FIN.
  // |compressed| specifies whether the frame should be compressed.
  // |headers| is the header block to include in the frame.
  SpdySynStreamControlFrame* CreateSynStream(SpdyStreamId stream_id,
                                             SpdyStreamId associated_stream_id,
                                             int priority,
                                             SpdyControlFlags flags,
                                             bool compressed,
                                             SpdyHeaderBlock* headers);

  // Create a SpdySynReplyControlFrame.
  // |stream_id| is the stream for this frame.
  // |flags| is the flags to use with the data.
  //    To mark this frame as the last frame, enable CONTROL_FLAG_FIN.
  // |compressed| specifies whether the frame should be compressed.
  // |headers| is the header block to include in the frame.
  SpdySynReplyControlFrame* CreateSynReply(SpdyStreamId stream_id,
                                           SpdyControlFlags flags,
                                           bool compressed,
                                           SpdyHeaderBlock* headers);

  static SpdyRstStreamControlFrame* CreateRstStream(SpdyStreamId stream_id,
                                                    SpdyStatusCodes status);

  // Creates an instance of SpdySettingsControlFrame. The SETTINGS frame is
  // used to communicate name/value pairs relevant to the communication channel.
  // TODO(mbelshe): add the name/value pairs!!
  static SpdySettingsControlFrame* CreateSettings(const SpdySettings& values);

  static SpdyControlFrame* CreateNopFrame();

  // Creates an instance of SpdyGoAwayControlFrame. The GOAWAY frame is used
  // prior to the shutting down of the TCP connection, and includes the
  // stream_id of the last stream the sender of the frame is willing to process
  // to completion.
  static SpdyGoAwayControlFrame* CreateGoAway(
      SpdyStreamId last_accepted_stream_id);

  // Creates an instance of SpdyHeadersControlFrame. The HEADERS frame is used
  // for sending additional headers outside of a SYN_STREAM/SYN_REPLY. The
  // arguments are the same as for CreateSynReply.
  SpdyHeadersControlFrame* CreateHeaders(SpdyStreamId stream_id,
                                         SpdyControlFlags flags,
                                         bool compressed,
                                         SpdyHeaderBlock* headers);

  // Creates an instance of SpdyWindowUpdateControlFrame. The WINDOW_UPDATE
  // frame is used to implement per stream flow control in SPDY.
  static SpdyWindowUpdateControlFrame* CreateWindowUpdate(
      SpdyStreamId stream_id,
      uint32 delta_window_size);

  // Given a SpdySettingsControlFrame, extract the settings.
  // Returns true on successful parse, false otherwise.
  static bool ParseSettings(const SpdySettingsControlFrame* frame,
      SpdySettings* settings);

  // Create a data frame.
  // |stream_id| is the stream  for this frame
  // |data| is the data to be included in the frame.
  // |len| is the length of the data
  // |flags| is the flags to use with the data.
  //    To create a compressed frame, enable DATA_FLAG_COMPRESSED.
  //    To mark this frame as the last data frame, enable DATA_FLAG_FIN.
  SpdyDataFrame* CreateDataFrame(SpdyStreamId stream_id, const char* data,
                                 uint32 len, SpdyDataFlags flags);

  // NOTES about frame compression.
  // We want spdy to compress headers across the entire session.  As long as
  // the session is over TCP, frames are sent serially.  The client & server
  // can each compress frames in the same order and then compress them in that
  // order, and the remote can do the reverse.  However, we ultimately want
  // the creation of frames to be less sensitive to order so that they can be
  // placed over a UDP based protocol and yet still benefit from some
  // compression.  We don't know of any good compression protocol which does
  // not build its state in a serial (stream based) manner....  For now, we're
  // using zlib anyway.

  // Compresses a SpdyFrame.
  // On success, returns a new SpdyFrame with the payload compressed.
  // Compression state is maintained as part of the SpdyFramer.
  // Returned frame must be freed with "delete".
  // On failure, returns NULL.
  SpdyFrame* CompressFrame(const SpdyFrame& frame);

  // Decompresses a SpdyFrame.
  // On success, returns a new SpdyFrame with the payload decompressed.
  // Compression state is maintained as part of the SpdyFramer.
  // Returned frame must be freed with "delete".
  // On failure, returns NULL.
  SpdyFrame* DecompressFrame(const SpdyFrame& frame);

  // Create a copy of a frame.
  // Returned frame must be freed with "delete".
  SpdyFrame* DuplicateFrame(const SpdyFrame& frame);

  // Returns true if a frame could be compressed.
  bool IsCompressible(const SpdyFrame& frame) const;

  // For debugging.
  static const char* StateToString(int state);
  static const char* ErrorCodeToString(int error_code);
  static void set_protocol_version(int version) { spdy_version_= version; }
  static int protocol_version() { return spdy_version_; }

  // Export the compression dictionary
  static const char kDictionary[];
  static const int kDictionarySize;

 protected:
  FRIEND_TEST_ALL_PREFIXES(SpdyFramerTest, DataCompression);
  FRIEND_TEST_ALL_PREFIXES(SpdyFramerTest, ExpandBuffer_HeapSmash);
  FRIEND_TEST_ALL_PREFIXES(SpdyFramerTest, HugeHeaderBlock);
  FRIEND_TEST_ALL_PREFIXES(SpdyFramerTest, UnclosedStreamDataCompressors);
  FRIEND_TEST_ALL_PREFIXES(SpdyFramerTest,
                           UncompressLargerThanFrameBufferInitialSize);
  friend class net::HttpNetworkLayer;  // This is temporary for the server.
  friend class net::HttpNetworkTransactionTest;
  friend class net::HttpProxyClientSocketPoolTest;
  friend class net::SpdyHttpStreamTest;
  friend class net::SpdyNetworkTransactionTest;
  friend class net::SpdyProxyClientSocketTest;
  friend class net::SpdySessionTest;
  friend class net::SpdyStreamTest;
  friend class test::TestSpdyVisitor;
  friend void test::FramerSetEnableCompressionHelper(SpdyFramer* framer,
                                                     bool compress);

  // For ease of testing we can tweak compression on/off.
  void set_enable_compression(bool value);
  static void set_enable_compression_default(bool value);


  // The initial size of the control frame buffer; this is used internally
  // as we parse through control frames. (It is exposed here for unit test
  // purposes.)
  static size_t kControlFrameBufferInitialSize;

  // The maximum size of the control frame buffer that we support.
  // TODO(mbelshe): We should make this stream-based so there are no limits.
  static size_t kControlFrameBufferMaxSize;

 private:
  typedef std::map<SpdyStreamId, z_stream*> CompressorMap;

  // Internal breakout from ProcessInput.  Returns the number of bytes
  // consumed from the data.
  size_t ProcessCommonHeader(const char* data, size_t len);
  void ProcessControlFrameHeader();
  size_t ProcessControlFramePayload(const char* data, size_t len);
  size_t ProcessDataFramePayload(const char* data, size_t len);

  // Get (and lazily initialize) the ZLib state.
  z_stream* GetHeaderCompressor();
  z_stream* GetHeaderDecompressor();
  z_stream* GetStreamCompressor(SpdyStreamId id);
  z_stream* GetStreamDecompressor(SpdyStreamId id);

  // Compression helpers
  SpdyControlFrame* CompressControlFrame(const SpdyControlFrame& frame);
  SpdyDataFrame* CompressDataFrame(const SpdyDataFrame& frame);
  SpdyControlFrame* DecompressControlFrame(const SpdyControlFrame& frame);
  SpdyDataFrame* DecompressDataFrame(const SpdyDataFrame& frame);
  SpdyFrame* CompressFrameWithZStream(const SpdyFrame& frame,
                                      z_stream* compressor);
  SpdyFrame* DecompressFrameWithZStream(const SpdyFrame& frame,
                                        z_stream* decompressor);
  void CleanupCompressorForStream(SpdyStreamId id);
  void CleanupDecompressorForStream(SpdyStreamId id);
  void CleanupStreamCompressorsAndDecompressors();

  // Not used (yet)
  size_t BytesSafeToRead() const;

  // Set the error code and moves the framer into the error state.
  void set_error(SpdyError error);

  // Expands the control frame buffer to accomodate a particular payload size.
  void ExpandControlFrameBuffer(size_t size);

  // Given a frame, breakdown the variable payload length, the static header
  // header length, and variable payload pointer.
  bool GetFrameBoundaries(const SpdyFrame& frame, int* payload_length,
                          int* header_length, const char** payload) const;

  int num_stream_compressors() const { return stream_compressors_.size(); }
  int num_stream_decompressors() const { return stream_decompressors_.size(); }

  SpdyState state_;
  SpdyError error_code_;
  size_t remaining_payload_;
  size_t remaining_control_payload_;

  char* current_frame_buffer_;
  size_t current_frame_len_;  // Number of bytes read into the current_frame_.
  size_t current_frame_capacity_;

  bool enable_compression_;  // Controls all compression
  // SPDY header compressors.
  scoped_ptr<z_stream> header_compressor_;
  scoped_ptr<z_stream> header_decompressor_;

  // Per-stream data compressors.
  CompressorMap stream_compressors_;
  CompressorMap stream_decompressors_;

  SpdyFramerVisitorInterface* visitor_;

  static bool compression_default_;
  static int spdy_version_;
};

}  // namespace spdy

#endif  // NET_SPDY_SPDY_FRAMER_H_