spdy 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -3,3 +3,4 @@
3
3
  Gemfile.lock
4
4
  pkg/*
5
5
  misc.md
6
+ .rvmrc
data/README.md CHANGED
@@ -4,39 +4,40 @@ SPDY was developed at Google as part of the "let's make the web faster" initiati
4
4
 
5
5
  Today, SPDY is built into Chrome + Google web-server infrastructure and is currently serving over 90% of all the SSL traffic. Yes, you read that right.. If you're using Chrome, and you're using Google products, chances are, you are fetching the content from Google servers over SPDY, not HTTP.
6
6
 
7
- See: [Life beyond HTTP 1.1: Google's SPDY](http://www.igvita.com/2011/04/07/life-beyond-http-11-googles-spdy)
7
+ * [Life beyond HTTP 1.1: Google's SPDY](http://www.igvita.com/2011/04/07/life-beyond-http-11-googles-spdy)
8
+ * [How to set up ruby, eventmachine and spdy to use NPN](https://gist.github.com/944386)
8
9
 
9
10
  ## Protocol Parser
10
11
 
11
12
  SPDY specification (Draft 2) defines its own framing and message exchange protocol which is layered on top of a raw TCP/SSL connection. This gem implements a basic, pure Ruby parser for the SPDY protocol:
12
13
 
13
- s = SPDY::Parser.new
14
+ ```ruby
15
+ s = SPDY::Parser.new
14
16
 
15
- s.on_headers_complete { |stream_id, associated_stream, priority, headers| ... }
16
- s.on_body { |stream_id, data| ... }
17
- s.on_message_complete { |stream_id| ... }
17
+ s.on_headers_complete { |stream_id, associated_stream, priority, headers| ... }
18
+ s.on_body { |stream_id, data| ... }
19
+ s.on_message_complete { |stream_id| ... }
18
20
 
19
- s << recieved_data
21
+ s << recieved_data
22
+ ```
20
23
 
21
24
  However, parsing the data is not enough, to do the full exchange you also have to respond to a SPDY client with appropriate 'control' and 'data' frames:
22
25
 
23
- sr = SPDY::Protocol::Control::SynReply.new
24
- headers = {'Content-Type' => 'text/plain', 'status' => '200 OK', 'version' => 'HTTP/1.1'}
25
- sr.create(:stream_id => 1, :headers => headers)
26
- send_data sr.to_binary_s
26
+ ```ruby
27
+ sr = SPDY::Protocol::Control::SynReply.new
28
+ headers = {'Content-Type' => 'text/plain', 'status' => '200 OK', 'version' => 'HTTP/1.1'}
29
+ sr.create(:stream_id => 1, :headers => headers)
30
+ send_data sr.to_binary_s
27
31
 
28
- # or, to send a data frame
32
+ # or, to send a data frame
29
33
 
30
- d = SPDY::Protocol::Data::Frame.new
31
- d.create(:stream_id => 1, :data => "This is SPDY.")
32
- send_data d.to_binary_s
34
+ d = SPDY::Protocol::Data::Frame.new
35
+ d.create(:stream_id => 1, :data => "This is SPDY.")
36
+ send_data d.to_binary_s
37
+ ```
33
38
 
34
39
  See example eventmachine server in *examples/spdy_server.rb* for a minimal SPDY "hello world" server.
35
40
 
36
- ## Todo:
37
-
38
- - implement support for all [control frames](https://sites.google.com/a/chromium.org/dev/spdy/spdy-protocol/spdy-protocol-draft2#TOC-Control-frames1)
39
-
40
41
  ### License
41
42
 
42
- (MIT License) - Copyright (c) 2011 Ilya Grigorik
43
+ MIT License - Copyright (c) 2011 Ilya Grigorik
@@ -9,21 +9,21 @@ class SPDYHandler < EM::Connection
9
9
  @parser.on_headers_complete do |stream_id, associated_stream, priority, headers|
10
10
  p [:SPDY_HEADERS, headers]
11
11
 
12
- sr = SPDY::Protocol::Control::SynReply.new
12
+ sr = SPDY::Protocol::Control::SynReply.new({:zlib_session => @parser.zlib_session})
13
13
  h = {'Content-Type' => 'text/plain', 'status' => '200 OK', 'version' => 'HTTP/1.1'}
14
- sr.create(:stream_id => 1, :headers => h)
14
+ sr.create({:stream_id => stream_id, :headers => h})
15
15
  send_data sr.to_binary_s
16
16
 
17
17
  p [:SPDY, :sent, :SYN_REPLY]
18
18
 
19
19
  d = SPDY::Protocol::Data::Frame.new
20
- d.create(:stream_id => 1, :data => "This is SPDY.")
20
+ d.create(:stream_id => stream_id, :data => "This is SPDY.")
21
21
  send_data d.to_binary_s
22
22
 
23
23
  p [:SPDY, :sent, :DATA]
24
24
 
25
25
  d = SPDY::Protocol::Data::Frame.new
26
- d.create(:stream_id => 1, :flags => 1)
26
+ d.create(:stream_id => stream_id, :flags => 1)
27
27
  send_data d.to_binary_s
28
28
 
29
29
  p [:SPDY, :sent, :DATA_FIN]
@@ -38,6 +38,7 @@ class SPDYHandler < EM::Connection
38
38
 
39
39
  def unbind
40
40
  p [:SPDY, :connection_closed]
41
+ @parser.zlib_session.reset
41
42
  end
42
43
  end
43
44
 
@@ -51,4 +52,4 @@ end
51
52
  # (2) start Chrome and force it to use SPDY over SSL.. on OSX:
52
53
  # > /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --use-spdy=ssl
53
54
  #
54
- # (3) visit https://localhost:1000/
55
+ # (3) visit https://localhost:10000/
@@ -1,6 +1,7 @@
1
1
  require 'bindata'
2
2
  require 'ffi/zlib'
3
3
 
4
+ require 'spdy/compat'
4
5
  require 'spdy/protocol'
5
6
  require 'spdy/compressor'
6
- require 'spdy/parser'
7
+ require 'spdy/parser'
@@ -0,0 +1,17 @@
1
+ if Object.instance_methods.include? "type"
2
+ class BinData::DSLMixin::DSLParser
3
+ def name_is_reserved_in_1_8?(name)
4
+ return false if name == "type"
5
+ name_is_reserved_in_1_9?(name)
6
+ end
7
+ alias_method :name_is_reserved_in_1_9?, :name_is_reserved?
8
+ alias_method :name_is_reserved?, :name_is_reserved_in_1_8?
9
+
10
+ def name_shadows_method_in_1_8?(name)
11
+ return false if name == "type"
12
+ name_shadows_method_in_1_9?(name)
13
+ end
14
+ alias_method :name_shadows_method_in_1_9?, :name_shadows_method?
15
+ alias_method :name_shadows_method?, :name_shadows_method_in_1_8?
16
+ end
17
+ end
@@ -1,5 +1,5 @@
1
1
  module SPDY
2
- module Zlib
2
+ class Zlib
3
3
 
4
4
  DICT = \
5
5
  "optionsgetheadpostputdeletetraceacceptaccept-charsetaccept-encodingaccept-" \
@@ -18,51 +18,62 @@ module SPDY
18
18
 
19
19
  CHUNK = 10*1024 # this is silly, but it'll do for now
20
20
 
21
- def self.inflate(data)
22
- in_buf = FFI::MemoryPointer.from_string(data)
23
- out_buf = FFI::MemoryPointer.new(CHUNK)
24
-
25
- zstream = FFI::Zlib::Z_stream.new
26
- zstream[:avail_in] = in_buf.size
27
- zstream[:avail_out] = CHUNK
28
- zstream[:next_in] = in_buf
29
- zstream[:next_out] = out_buf
30
-
31
- result = FFI::Zlib.inflateInit(zstream)
21
+ def initialize
22
+ @inflate_zstream = FFI::Zlib::Z_stream.new
23
+ result = FFI::Zlib.inflateInit(@inflate_zstream)
32
24
  raise "invalid stream" if result != FFI::Zlib::Z_OK
33
25
 
34
- result = FFI::Zlib.inflate(zstream, FFI::Zlib::Z_SYNC_FLUSH)
35
- raise "invalid stream" if result != FFI::Zlib::Z_NEED_DICT
26
+ @deflate_zstream = FFI::Zlib::Z_stream.new
27
+ result = FFI::Zlib.deflateInit(@deflate_zstream, FFI::Zlib::Z_DEFAULT_COMPRESSION)
28
+ raise "invalid stream" if result != FFI::Zlib::Z_OK
36
29
 
37
- result = FFI::Zlib.inflateSetDictionary(zstream, DICT, DICT.size)
30
+ result = FFI::Zlib.deflateSetDictionary(@deflate_zstream, DICT, DICT.size)
38
31
  raise "invalid dictionary" if result != FFI::Zlib::Z_OK
32
+ end
39
33
 
40
- result = FFI::Zlib.inflate(zstream, FFI::Zlib::Z_SYNC_FLUSH)
41
- raise "cannot inflate" if result != FFI::Zlib::Z_OK
34
+ def reset
35
+ result = FFI::Zlib.inflateReset(@inflate_zstream)
36
+ raise "invalid stream" if result != FFI::Zlib::Z_OK
42
37
 
43
- out_buf.get_bytes(0, zstream[:total_out])
38
+ result = FFI::Zlib.deflateReset(@deflate_zstream)
39
+ raise "invalid stream" if result != FFI::Zlib::Z_OK
44
40
  end
45
41
 
46
- def self.deflate(data)
42
+ def inflate(data)
47
43
  in_buf = FFI::MemoryPointer.from_string(data)
48
44
  out_buf = FFI::MemoryPointer.new(CHUNK)
49
45
 
50
- zstream = FFI::Zlib::Z_stream.new
51
- zstream[:avail_in] = in_buf.size-1
52
- zstream[:avail_out] = CHUNK
53
- zstream[:next_in] = in_buf
54
- zstream[:next_out] = out_buf
46
+ @inflate_zstream[:avail_in] = in_buf.size-1
47
+ @inflate_zstream[:avail_out] = CHUNK
48
+ @inflate_zstream[:next_in] = in_buf
49
+ @inflate_zstream[:next_out] = out_buf
55
50
 
56
- result = FFI::Zlib.deflateInit(zstream, -1)
57
- raise "invalid stream" if result != FFI::Zlib::Z_OK
51
+ result = FFI::Zlib.inflate(@inflate_zstream, FFI::Zlib::Z_SYNC_FLUSH)
52
+ if result == FFI::Zlib::Z_NEED_DICT
53
+ result = FFI::Zlib.inflateSetDictionary(@inflate_zstream, DICT, DICT.size)
54
+ raise "invalid dictionary" if result != FFI::Zlib::Z_OK
55
+
56
+ result = FFI::Zlib.inflate(@inflate_zstream, FFI::Zlib::Z_SYNC_FLUSH)
57
+ end
58
58
 
59
- result = FFI::Zlib.deflateSetDictionary(zstream, DICT, DICT.size)
60
- raise "invalid dictionary" if result != FFI::Zlib::Z_OK
59
+ raise "cannot inflate" if result != FFI::Zlib::Z_OK && result != FFI::Zlib::Z_STREAM_END
60
+
61
+ out_buf.get_bytes(0, CHUNK - @inflate_zstream[:avail_out])
62
+ end
63
+
64
+ def deflate(data)
65
+ in_buf = FFI::MemoryPointer.from_string(data)
66
+ out_buf = FFI::MemoryPointer.new(CHUNK)
67
+
68
+ @deflate_zstream[:avail_in] = in_buf.size-1
69
+ @deflate_zstream[:avail_out] = CHUNK
70
+ @deflate_zstream[:next_in] = in_buf
71
+ @deflate_zstream[:next_out] = out_buf
61
72
 
62
- result = FFI::Zlib.deflate(zstream, FFI::Zlib::Z_SYNC_FLUSH)
73
+ result = FFI::Zlib.deflate(@deflate_zstream, FFI::Zlib::Z_SYNC_FLUSH)
63
74
  raise "cannot deflate" if result != FFI::Zlib::Z_OK
64
75
 
65
- out_buf.get_bytes(0, zstream[:total_out])
76
+ out_buf.get_bytes(0, CHUNK - @deflate_zstream[:avail_out])
66
77
  end
67
78
  end
68
79
  end
@@ -2,8 +2,11 @@ module SPDY
2
2
  class Parser
3
3
  include Protocol
4
4
 
5
+ attr :zlib_session
6
+
5
7
  def initialize
6
8
  @buffer = ''
9
+ @zlib_session = Zlib.new
7
10
  end
8
11
 
9
12
  def <<(data)
@@ -30,7 +33,7 @@ module SPDY
30
33
 
31
34
  headers = {}
32
35
  if pckt.data.size > 0
33
- data = Zlib.inflate(pckt.data.to_s)
36
+ data = @zlib_session.inflate(pckt.data.to_s)
34
37
  headers = NV.new.read(data).to_h
35
38
  end
36
39
 
@@ -53,19 +56,21 @@ module SPDY
53
56
 
54
57
  case pckt.type.to_i
55
58
  when 1 then # SYN_STREAM
56
- pckt = Control::SynStream.new
59
+ pckt = Control::SynStream.new({:zlib_session => @zlib_session})
57
60
  unpack_control(pckt, @buffer)
58
61
 
62
+ @on_message_complete.call(pckt.header.stream_id) if @on_message_complete && fin?(pckt.header)
63
+
59
64
  when 2 then # SYN_REPLY
60
- pckt = Control::SynReply.new
65
+ pckt = Control::SynReply.new({:zlib_session => @zlib_session})
61
66
  unpack_control(pckt, @buffer)
62
67
 
68
+ @on_message_complete.call(pckt.header.stream_id) if @on_message_complete && fin?(pckt.header)
69
+
63
70
  else
64
71
  raise 'invalid control frame'
65
72
  end
66
73
 
67
- @on_message_complete.call(pckt.header.stream_id) if @on_message_complete && fin?(pckt.header)
68
-
69
74
  when DATA_BIT
70
75
  return if @buffer.size < 8
71
76
 
@@ -5,13 +5,24 @@ module SPDY
5
5
  DATA_BIT = 0
6
6
  VERSION = 2
7
7
 
8
+ SETTINGS_UPLOAD_BANDWIDTH = 1
9
+ SETTINGS_DOWNLOAD_BANDWIDTH = 2
10
+ SETTINGS_ROUND_TRIP_TIME = 3
11
+ SETTINGS_MAX_CONCURRENT_STREAMS = 4
12
+ SETTINGS_CURRENT_CWND = 5
13
+
8
14
  module Control
9
15
  module Helpers
16
+ def initialize_instance
17
+ super
18
+ @zlib_session = @params[:zlib_session]
19
+ end
20
+
10
21
  def parse(chunk)
11
22
  head = Control::Header.new.read(chunk)
12
23
  self.read(chunk)
13
24
 
14
- data = Zlib.inflate(self.data.to_s)
25
+ data = @zlib_session.inflate(self.data.to_s)
15
26
  self.uncompressed_data = NV.new.read(data)
16
27
  self
17
28
  end
@@ -26,7 +37,7 @@ module SPDY
26
37
  nv = SPDY::Protocol::NV.new
27
38
  nv.create(opts[:headers])
28
39
 
29
- nv = SPDY::Zlib.deflate(nv.to_binary_s)
40
+ nv = @zlib_session.deflate(nv.to_binary_s)
30
41
  self.header.len = self.header.len.to_i + nv.size
31
42
 
32
43
  self.data = nv
@@ -82,6 +93,121 @@ module SPDY
82
93
  build({:type => 2, :len => 6}.merge(opts))
83
94
  end
84
95
  end
96
+
97
+ class RstStream < BinData::Record
98
+ hide :u1
99
+
100
+ bit1 :frame, :initial_value => CONTROL_BIT
101
+ bit15 :version, :initial_value => VERSION
102
+ bit16 :type, :value => 3
103
+
104
+ bit8 :flags, :value => 0
105
+ bit24 :len, :value => 8
106
+
107
+ bit1 :u1
108
+ bit31 :stream_id
109
+
110
+ bit32 :status_code
111
+
112
+ def parse(chunk)
113
+ self.read(chunk)
114
+ self
115
+ end
116
+
117
+ def create(opts = {})
118
+ self.stream_id = opts.fetch(:stream_id, 1)
119
+ self.status_code = opts.fetch(:status_code, 5)
120
+ self
121
+ end
122
+ end
123
+
124
+ class Settings < BinData::Record
125
+ bit1 :frame, :initial_value => CONTROL_BIT
126
+ bit15 :version, :initial_value => VERSION
127
+ bit16 :type, :value => 4
128
+
129
+ bit8 :flags
130
+ bit24 :len, :value => lambda { pairs * 8 }
131
+ bit32 :pairs
132
+
133
+ array :headers, :initial_length => :pairs do
134
+ bit32 :id_data
135
+ bit32 :value_data
136
+ end
137
+
138
+ def parse(chunk)
139
+ self.read(chunk)
140
+ self
141
+ end
142
+
143
+ def create(opts = {})
144
+ opts.each do |k, v|
145
+ key = SPDY::Protocol.const_get(k.to_s.upcase)
146
+ self.headers << { :id_data => key , :value_data => v }
147
+ end
148
+ self.pairs = opts.size
149
+ self
150
+ end
151
+ end
152
+
153
+ class Ping < BinData::Record
154
+ bit1 :frame, :initial_value => CONTROL_BIT
155
+ bit15 :version, :initial_value => VERSION
156
+ bit16 :type, :value => 6
157
+
158
+ bit8 :flags, :value => 0
159
+ bit24 :len, :value => 4
160
+
161
+ bit32 :ping_id
162
+
163
+ def parse(chunk)
164
+ self.read(chunk)
165
+ self
166
+ end
167
+
168
+ def create(opts = {})
169
+ self.ping_id = opts.fetch(:ping_id, 1)
170
+ self
171
+ end
172
+ end
173
+
174
+ class Goaway < BinData::Record
175
+ hide :u1
176
+
177
+ bit1 :frame, :initial_value => CONTROL_BIT
178
+ bit15 :version, :initial_value => VERSION
179
+ bit16 :type, :value => 7
180
+
181
+ bit8 :flags, :value => 0
182
+ bit24 :len, :value => 4
183
+
184
+ bit1 :u1
185
+ bit31 :stream_id
186
+
187
+ def parse(chunk)
188
+ self.read(chunk)
189
+ self
190
+ end
191
+
192
+ def create(opts = {})
193
+ self.stream_id = opts.fetch(:stream_id, 1)
194
+ self
195
+ end
196
+ end
197
+
198
+
199
+ class Headers < BinData::Record
200
+ attr_accessor :uncompressed_data
201
+ include Helpers
202
+
203
+ header :header
204
+ bit16 :unused
205
+ string :data, :read_length => lambda { header.len - 6 }
206
+
207
+ def create(opts = {})
208
+ build({:type => 8, :len => 6}.merge(opts))
209
+ end
210
+ end
85
211
  end
86
212
 
87
213
  module Data
@@ -1,3 +1,3 @@
1
1
  module Spdy
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
  end
@@ -2,13 +2,24 @@ require 'helper'
2
2
 
3
3
  describe SPDY::Zlib do
4
4
  it "should inflate header with custom dictionary" do
5
- SPDY::Zlib.inflate(COMPRESSED_HEADER).should match('HTTP/1.1')
5
+ zlib_session = SPDY::Zlib.new
6
+ zlib_session.inflate(COMPRESSED_HEADER).should match('HTTP/1.1')
6
7
  end
7
8
 
8
9
  it "should deflate header with custom dictionary" do
9
- orig = SPDY::Zlib.inflate(COMPRESSED_HEADER)
10
- rinse = SPDY::Zlib.inflate(SPDY::Zlib.deflate(orig))
10
+ zlib_session = SPDY::Zlib.new
11
11
 
12
+ orig = zlib_session.inflate(COMPRESSED_HEADER)
13
+ zlib_session.reset
14
+
15
+ rinse = zlib_session.inflate(zlib_session.deflate(orig))
12
16
  orig.should == rinse
13
17
  end
14
- end
18
+
19
+ it "can deflate multiple packets" do
20
+ zlib_session = SPDY::Zlib.new
21
+
22
+ zlib_session.inflate(COMPRESSED_HEADER_1)
23
+ zlib_session.inflate(COMPRESSED_HEADER_2).should == UNCOMPRESSED_HEADER_2
24
+ end
25
+ end
@@ -3,13 +3,29 @@ require 'spdy'
3
3
 
4
4
  COMPRESSED_HEADER = "8\xEA\xDF\xA2Q\xB2b\xE0f`\x83\xA4\x17\x86(4\xDBu0\xAC\xD7\x06\x89\xC2\xFDa]hk\xA0g\xA9\x83p\x13X\xC0B\a\xEE?\x1D-}-\xB0\x98)\x03\x1Fjff\x90\xF3\f\xF6\x87:U\a\xECV\xB0:s\x1D\x88zc\x06~\xB4\xEC\xCE \b\xF2\x8C\x0E\xD47:\xC5)\xC9\x19p5\xB0\x14\xC2\xC0\x97\x9A\xA7\e\x1A\xAC\x93\nu\b\x03\e$V\x19Z\x8A\x8D\x8C\xD3\xD2u\x8D\v\x8C\xCC\x8D\x92\v,\xCB\r\xE2\x8Bl\xCD\xAD\x15\f\xB3\xCD\v\xCDu3\rR\xCC\xD3\x8B\v\r-\xCC\x81\xA2\x06\xD6\n\xF1 '\x96$\xA5&\x96\x18\x01d[\x9Cj\x9CUQ\x92dT\x99e\x9C\x9A\x93\x93j\f\x94\x8D//)\x8F/\xCB,\x8E\afy[k\x85\xB2\xC4\xBC\xCC\x92\xCA\xF8\xCC\x14\xDB4c#\x8B\xE4$3\x13c\x93d`\xFEM12N154OI32O\x03\x16\x04\xA6\x96I\f,\xA0\xC2\x88\x81\x0F\x94bs@L+K\x03\x03\x03\x06\xB6\\`!\x98\x9F\xC2\xC0\xEC\xEE\x1A\xC2\xC0V\f\xCC7\xB9\xA9\f\xAC\x19%%\x05\xC5\f\xCC\xA0\bb\xD4g\xE0B\x94*\fe\xBE\xF9U\x9999\x89\xFA\xA6z\x06\x00)h\xF8&&g\xE6\x95\xE4\x17gX+x\x02\x13z\x8E\x02P@\xC1?X!B\xC1\xD0 \xDE,\xDE\\S\xC1\x11\x18\x87\xA9\xE1\xA9I\xDE\x99%\xFA\xA6\xC6&zF&\n\x1A\xDE\x1E!\xBE>:\n9\x99\xD9\xA9\n\xEE\xA9\xC9\xD9\xF9\x9A\n\xCE\x19\xC0\xD22U\xDF\xD0P\xCF@\xCF\xCC\xD2L\xCF\xC8B!81-\xB1(\x13\xAA\x89\x81\x1D\x9Af\x188`I\t\x00\x00\x00\xFF\xFF"
5
5
 
6
+ COMPRESSED_HEADER_1 = "8\xEA\xDF\xA2Q\xB2b\xE0b`\x83\xA4\x17\x06{\xB8\vu0,\xD6\xAE@\x17\xCD\xCD\xB1.\xB45\xD0\xB3\xD4\xD1\xD2\xD7\x02\xB3,\x18\xF8Ps,\x83\x9Cg\xB0?\xD4=:`\a\x81\xD5\x99\xEB@\xD4\e3\xF0\xA3\xE5i\x06A\x90\x8Bu\xA0N\xD6)NI\xCE\x80\xAB\x81%\x03\x06\xBE\xD4<\xDD\xD0`\x9D\xD4<\xA8\xA5,\xA0<\xCE\xC0\x0FJ\b9 \xA6\x150\xE3\x19\x180\xB0\xE5\x02\v\x97\xFC\x14\x06fw\xD7\x10\x06\xB6b`z\xCCMe`\xCD())(f`\x06y\x9CQ\x9F\x81\v\x91[\x19\xD2}\xF3\xAB2sr\x12\xF5M\xF5\f\x144\x00\x8A04\xB4V\xF0\xC9\xCC+\xADP\xA8\xB00\x8B73\xD1Tp\x04z>5<5\xC9;\xB3D\xDF\xD4\xD8D\xCF\x18\xA8\xCC\xDB#\xC4\xD7GG!'3;U\xC1=59;_S\xC19\x03X\xEC\xA4\xEA\e\x1A\xE9\x01}jb\x04R\x16\x9C\x98\x96X\x94\t\xD5\xC4\xC0\x0E\r|\x06\x0EX\x9C\x00\x00\x00\x00\xFF\xFF"
7
+
8
+ COMPRESSED_HEADER_2 = "B\x8A\x02f``\x0E\xAD`\xE4\xD1OK,\xCB\x04f3= 1XB\x14\x00\x00\x00\xFF\xFF"
9
+
10
+ UNCOMPRESSED_HEADER_1 = "\x00\n\x00\x06accept\x00?text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\x00\x0eaccept-charset\x00\x1eISO-8859-1,utf-8;q=0.7,*;q=0.3\x00\x0faccept-encoding\x00\x11gzip,deflate,sdch\x00\x0faccept-language\x00\x0een-US,en;q=0.8\x00\x04host\x00\x0flocalhost:10000\x00\x06method\x00\x03GET\x00\x06scheme\x00\x05https\x00\x03url\x00\x01/\x00\nuser-agent\x00gMozilla/5.0 (X11; Linux x86_64) AppleWebKit/534.30 (KHTML, like Gecko) Chrome/12.0.742.30 Safari/534.30\x00\x07version\x00\x08HTTP/1.1"
11
+
12
+ UNCOMPRESSED_HEADER_2 = "\x00\n\x00\x06accept\x00\x03*/*\x00\x0eaccept-charset\x00\x1eISO-8859-1,utf-8;q=0.7,*;q=0.3\x00\x0faccept-encoding\x00\x11gzip,deflate,sdch\x00\x0faccept-language\x00\x0een-US,en;q=0.8\x00\x04host\x00\x0flocalhost:10000\x00\x06method\x00\x03GET\x00\x06scheme\x00\x05https\x00\x03url\x00\x0c/favicon.ico\x00\nuser-agent\x00gMozilla/5.0 (X11; Linux x86_64) AppleWebKit/534.30 (KHTML, like Gecko) Chrome/12.0.742.30 Safari/534.30\x00\x07version\x00\x08HTTP/1.1"
13
+
6
14
  SYN_STREAM = "\x80\x02\x00\x01\x01\x00\x01E\x00\x00\x00\x01\x00\x00\x00\x00\x00\x008\xEA\xDF\xA2Q\xB2b\xE0f`\x83\xA4\x17\x86(4\xDBu0\xAC\xD7\x06\x89\xC2\xFDa]hk\xA0g\xA9\x83p\x13X\xC0B\a\xEE?\x1D-}-\xB0\x98)\x03\x1Fjff\x90\xF3\f\xF6\x87:U\a\xECV\xB0:s\x1D\x88zc\x06~\xB4\xEC\xCE \b\xF2\x8C\x0E\xD47:\xC5)\xC9\x19p5\xB0\x14\xC2\xC0\x97\x9A\xA7\e\x1A\xAC\x93\nu\b\x03/J:d\xE0\x84\x86\x96\xAD\x01\x03\v\xA8``\xE0342\xD73\x00BC+K\x03\x03\x03\x06\xB6\\`\x81\x94\x9F\xC2\xC0\xEC\xEE\x1A\xC2\xC0V\f\xD4\x9B\x9B\xCA\xC0\x9AQRRP\xCC\xC0\f\n,\x11}{\x80\x80a\x9Do\x9B\xA8\x06,\x10\x80\xC5\x86mVq~\x1E\x03\x17\"\xD33\x94\xF9\xE6We\xE6\xE4$\xEA\x9B\xEA\x19(h\xF8&&g\xE6\x95\xE4\x17gX+x\x02\xD3a\x8E\x02P@\xC1?X!B\xC1\xD0 \xDE,\xDE\\S\xC1\x11\x18\xC4\xA9\xE1\xA9I\xDE\x99%\xFA\xA6\xC6&zF&\n\x1A\xDE\x1E!\xBE>:\n9\x99\xD9\xA9\n\xEE\xA9\xC9\xD9\xF9\x9A\n\xCE\x19\xC0\xC2,U\xDF\xD0\x10\xE8X3K3=#\v\x85\xE0\xC4\xB4\xC4\xA2L\xA8&\x06vh\x942p\xC0b\x1A\x00\x00\x00\xFF\xFF"
7
15
 
8
16
  SYN_REPLY = "\x80\x02\x00\x02\x00\x00\x005\x00\x00\x00\x01\x00\x00x\xbb\xdf\xa2Q\xb2b`f\xe0q\x86\x06R\x080\x90\x18\xb8\x10v0\xb0A\x943\xb0\x01\x93\xb1\x82\xbf7\x03;T#\x03\x07\xcc<\x00\x00\x00\x00\xff\xff"
9
17
 
10
18
  RST_STREAM = "\x80\x02\x00\x03\x00\x00\x00\b\x00\x00\x00\x01\x00\x00\x00\x01"
11
19
 
20
+ SETTINGS = "\x80\x02\x00\x04\x00\x00\x00\b\x00\x00\x00\x01\x00\x00\x00\x03\x00\x00\x01,"
21
+
12
22
  DATA = "\x00\x00\x00\x01\x00\x00\x00\rThis is SPDY."
13
23
  DATA_FIN = "\x00\x00\x00\x01\x01\x00\x00\x00"
14
24
 
15
- NV = "\x00\x03\x00\x0cContent-Type\x00\ntext/plain\x00\x06status\x00\x06200 OK\x00\x07version\x00\x08HTTP/1.1"
25
+ NV = "\x00\x03\x00\x0cContent-Type\x00\ntext/plain\x00\x06status\x00\x06200 OK\x00\x07version\x00\x08HTTP/1.1"
26
+
27
+ PING = "\x80\x02\x00\x06\x00\x00\x00\x04\x00\x00\x00\x01"
28
+
29
+ GOAWAY = "\x80\x02\x00\x07\x00\x00\x00\x04\x00\x00\x00\x01"
30
+
31
+ HEADERS = "\x80\x02\x00\x08\x01\x00\x01A\x00\x00\x00\x01\x00\x008\xEA\xDF\xA2Q\xB2b\xE0f`\x83\xA4\x17\x86(4\xDBu0\xAC\xD7\x06\x89\xC2\xFDa]hk\xA0g\xA9\x83p\x13X\xC0B\a\xEE?\x1D-}-\xB0\x98)\x03\x1Fjff\x90\xF3\f\xF6\x87:U\a\xECV\xB0:s\x1D\x88zc\x06~\xB4\xEC\xCE \b\xF2\x8C\x0E\xD47:\xC5)\xC9\x19p5\xB0\x14\xC2\xC0\x97\x9A\xA7\e\x1A\xAC\x93\nu\b\x03/J:d\xE0\x84\x86\x96\xAD\x01\x03\v\xA8``\xE0342\xD73\x00BC+K\x03\x03\x03\x06\xB6\\`\x81\x94\x9F\xC2\xC0\xEC\xEE\x1A\xC2\xC0V\f\xD4\x9B\x9B\xCA\xC0\x9AQRRP\xCC\xC0\f\n,\x11}{\x80\x80a\x9Do\x9B\xA8\x06,\x10\x80\xC5\x86mVq~\x1E\x03\x17\"\xD33\x94\xF9\xE6We\xE6\xE4$\xEA\x9B\xEA\x19(h\xF8&&g\xE6\x95\xE4\x17gX+x\x02\xD3a\x8E\x02P@\xC1?X!B\xC1\xD0 \xDE,\xDE\\S\xC1\x11\x18\xC4\xA9\xE1\xA9I\xDE\x99%\xFA\xA6\xC6&zF&\n\x1A\xDE\x1E!\xBE>:\n9\x99\xD9\xA9\n\xEE\xA9\xC9\xD9\xF9\x9A\n\xCE\x19\xC0\xC2,U\xDF\xD0\x10\xE8X3K3=#\v\x85\xE0\xC4\xB4\xC4\xA2L\xA8&\x06vh\x942p\xC0b\x1A\x00\x00\x00\xFF\xFF"
@@ -1,109 +1,404 @@
1
1
  require 'helper'
2
2
 
3
3
  describe SPDY::Protocol do
4
+ context "data frames" do
5
+ describe "DATA" do
6
+ it "should create a data frame" do
7
+ data = "This is SPDY."
4
8
 
5
- context "NV" do
6
- it "should create an NV packet" do
7
- nv = SPDY::Protocol::NV.new
8
- nv.create({'Content-Type' => 'text/plain', 'status' => '200 OK', 'version' => 'HTTP/1.1'})
9
+ d = SPDY::Protocol::Data::Frame.new
10
+ d.create(:stream_id => 1, :data => data)
11
+
12
+ d.to_binary_s.should == DATA
13
+ end
14
+
15
+ it "should create a FIN data frame" do
16
+ d = SPDY::Protocol::Data::Frame.new
17
+ d.create(:stream_id => 1, :flags => 1)
18
+
19
+ d.to_binary_s.should == DATA_FIN
20
+ end
21
+
22
+ it "should read a FIN data frame" do
23
+ d = SPDY::Protocol::Data::Frame.new
24
+ d.create(:stream_id => 1, :flags => 1)
25
+
26
+ d.to_binary_s.should == DATA_FIN
27
+ pckt = SPDY::Protocol::Data::Frame.new.read(d.to_binary_s)
28
+ pckt.flags.should == 1
29
+ end
9
30
 
10
- nv.to_binary_s.should == NV
11
31
  end
12
32
  end
13
33
 
14
- context "SYN_STREAM" do
15
- it "should create a SYN_STREAM packet" do
16
- sr = SPDY::Protocol::Control::SynStream.new
34
+ context "control frames" do
35
+ describe "SYN_STREAM" do
36
+ it "should create a SYN_STREAM packet" do
37
+ zlib_session = SPDY::Zlib.new
17
38
 
18
- headers = {
19
- "accept"=>"application/xml", "host"=>"127.0.0.1:9000",
20
- "method"=>"GET", "scheme"=>"https",
21
- "url"=>"/?echo=a&format=json","version"=>"HTTP/1.1"
22
- }
39
+ sr = SPDY::Protocol::Control::SynStream.new({:zlib_session => zlib_session})
23
40
 
24
- sr.create(:stream_id => 1, :headers => headers)
25
- sr.header.version.should == 2
26
- sr.pri.should == 0
41
+ headers = {
42
+ "accept"=>"application/xml", "host"=>"127.0.0.1:9000",
43
+ "method"=>"GET", "scheme"=>"https",
44
+ "url"=>"/?echo=a&format=json","version"=>"HTTP/1.1"
45
+ }
27
46
 
28
- sr.header.len.should > 50
29
- sr.data.should_not be_nil
47
+ sr.create({:stream_id => 1, :headers => headers})
48
+ sr.header.version.should == 2
49
+ sr.pri.should == 0
30
50
 
31
- st = SPDY::Protocol::Control::SynStream.new
32
- st.parse(sr.to_binary_s)
33
- st.num_bytes.should == sr.to_binary_s.size
34
- end
51
+ sr.header.len.should > 50
52
+ sr.data.should_not be_nil
35
53
 
36
- it "should parse SYN_STREAM packet" do
37
- sr = SPDY::Protocol::Control::SynStream.new
38
- sr.parse(SYN_STREAM)
54
+ st = SPDY::Protocol::Control::SynStream.new({:zlib_session => zlib_session})
55
+ st.parse(sr.to_binary_s)
56
+ st.num_bytes.should == sr.to_binary_s.size
57
+ end
39
58
 
40
- sr.num_bytes.should == SYN_STREAM.size
59
+ it "should parse SYN_STREAM packet" do
60
+ zlib_session = SPDY::Zlib.new
41
61
 
42
- sr.header.type.should == 1
43
- sr.uncompressed_data.to_h.class.should == Hash
44
- sr.uncompressed_data.to_h['method'].should == 'GET'
62
+ sr = SPDY::Protocol::Control::SynStream.new({:zlib_session => zlib_session})
63
+ sr.parse(SYN_STREAM)
45
64
 
46
- sr.to_binary_s.should == SYN_STREAM
65
+ sr.num_bytes.should == SYN_STREAM.size
66
+
67
+ sr.header.type.should == 1
68
+ sr.uncompressed_data.to_h.class.should == Hash
69
+ sr.uncompressed_data.to_h['method'].should == 'GET'
70
+
71
+ sr.to_binary_s.should == SYN_STREAM
72
+ end
47
73
  end
48
- end
49
74
 
50
- context "SYN_REPLY" do
51
- it "should create a SYN_REPLY packet" do
52
- sr = SPDY::Protocol::Control::SynReply.new
75
+ describe "SYN_REPLY" do
76
+ describe "creating a packet" do
77
+ before do
78
+ zlib_session = SPDY::Zlib.new
79
+
80
+ @sr = SPDY::Protocol::Control::SynReply.new({:zlib_session => zlib_session})
81
+
82
+ headers = {'Content-Type' => 'text/plain', 'status' => '200 OK', 'version' => 'HTTP/1.1'}
83
+ @sr.create({:stream_id => 1, :headers => headers})
84
+ end
53
85
 
54
- headers = {'Content-Type' => 'text/plain', 'status' => '200 OK', 'version' => 'HTTP/1.1'}
55
- sr.create(:stream_id => 1, :headers => headers)
86
+ describe "common control frame fields" do
87
+ it "is version 2" do
88
+ @sr.header.version.should == 2
89
+ end
90
+ it "is type 2" do
91
+ @sr.header.type.should == 2
92
+ end
93
+ it "has empty flags" do
94
+ @sr.header.flags.should == 0
95
+ end
96
+ end
56
97
 
57
- sr.header.version.should == 2
58
- sr.header.stream_id.should == 1
98
+ describe "type specific frame fields" do
99
+ it "has a stream id" do
100
+ @sr.header.stream_id.should == 1
101
+ end
102
+ it "has data" do
103
+ @sr.data.should_not be_nil
104
+ end
105
+ specify { @sr.header.len.should > 50 }
106
+ end
59
107
 
60
- sr.header.len.should > 50
61
- sr.data.should_not be_nil
108
+ describe "assembled packet" do
109
+ before do
110
+ @packet = @sr.to_binary_s
111
+ end
62
112
 
63
- sr.to_binary_s.should == SYN_REPLY
113
+ specify "starts with a control bit" do
114
+ @packet[0...1].should == "\x80"
115
+ end
116
+ specify "followed by the version" do
117
+ @packet[1...2].should == "\x02"
118
+ end
119
+ specify "followed by the type" do
120
+ @packet[2..3].should == "\x00\x02"
121
+ end
122
+ specify "followed by flags" do
123
+ @packet[4...5].should == "\x00"
124
+ end
125
+ specify "followed by the length" do
126
+ @packet[5..7].should == "\x00\x005"
127
+ end
128
+ specify "followed by the stream ID" do
129
+ @packet[8..11].should == "\x00\x00\x00\x01"
130
+ end
131
+ specify "followed by unused space" do
132
+ @packet[12..13].should == "\x00\x00"
133
+ end
134
+ specify "followed by compressed NV data" do
135
+ zlib_session = SPDY::Zlib.new
136
+
137
+ data = zlib_session.inflate(@packet[14..-1].to_s)
138
+ data.should =~ %r{\x00\x0cContent-Type}
139
+ end
140
+ end
141
+
142
+ end
143
+
144
+ it "should parse SYN_REPLY packet" do
145
+ zlib_session = SPDY::Zlib.new
146
+
147
+ sr = SPDY::Protocol::Control::SynReply.new({:zlib_session => zlib_session})
148
+ sr.parse(SYN_REPLY)
149
+
150
+ sr.header.type.should == 2
151
+ sr.uncompressed_data.to_h.class.should == Hash
152
+ sr.uncompressed_data.to_h['status'].should == '200 OK'
153
+
154
+ sr.to_binary_s.should == SYN_REPLY
155
+ end
64
156
  end
65
157
 
66
- it "should parse SYN_REPLY packet" do
67
- sr = SPDY::Protocol::Control::SynReply.new
68
- sr.parse(SYN_REPLY)
158
+ describe "RST_STREAM" do
159
+ it "can parse a reset packet" do
160
+ ping = SPDY::Protocol::Control::RstStream.new
161
+ ping.parse(RST_STREAM)
69
162
 
70
- sr.header.type.should == 2
71
- sr.uncompressed_data.to_h.class.should == Hash
72
- sr.uncompressed_data.to_h['status'].should == '200 OK'
163
+ ping.stream_id.should == 1
164
+ ping.type.should == 3
73
165
 
74
- sr.to_binary_s.should == SYN_REPLY
166
+ ping.to_binary_s.should == RST_STREAM
167
+ end
168
+
169
+ describe "the assembled packet" do
170
+ before do
171
+ @rs = SPDY::Protocol::Control::RstStream.new
172
+ @rs.create(:stream_id => 1, :status_code => 1)
173
+ @frame = Array(@rs.to_binary_s.bytes)
174
+ end
175
+ specify "starts with a control bit" do
176
+ @frame[0].should == 128
177
+ end
178
+ specify "followed by the version (2)" do
179
+ @frame[1].should == 2
180
+ end
181
+ specify "followed by the type (3)" do
182
+ @frame[2..3].should == [0,3]
183
+ end
184
+ specify "followed by flags (0)" do
185
+ @frame[4].should == 0
186
+ end
187
+ specify "followed by the length (always 8)" do
188
+ @frame[5..7].should == [0,0,8]
189
+ end
190
+ specify "followed by the status code" do
191
+ @frame[8..11].should == [0,0,0,1]
192
+ end
193
+ end
75
194
  end
76
- end
77
195
 
78
- context "DATA" do
79
- it "should create a data frame" do
80
- data = "This is SPDY."
196
+ describe "SETTINGS" do
197
+ it "can parse a SETTINGS packet" do
198
+ settings = SPDY::Protocol::Control::Settings.new
199
+ settings.parse(SETTINGS)
200
+
201
+ settings.type.should == 4
202
+ settings.pairs.should == 1
203
+
204
+ settings.headers[0].id_data.should == SPDY::Protocol::SETTINGS_ROUND_TRIP_TIME
205
+ settings.headers[0].value_data.should == 300
206
+
207
+ settings.to_binary_s.should == SETTINGS
208
+ end
81
209
 
82
- d = SPDY::Protocol::Data::Frame.new
83
- d.create(:stream_id => 1, :data => data)
210
+ describe "the assembled packet" do
211
+ before do
212
+ @settings = SPDY::Protocol::Control::Settings.new
213
+ @settings.create(:settings_round_trip_time => 300)
214
+ @frame = Array(@settings.to_binary_s.bytes)
215
+ end
216
+ specify "starts with a control bit" do
217
+ @frame[0].should == 128
218
+ end
219
+ specify "followed by the version (2)" do
220
+ @frame[1].should == 2
221
+ end
222
+ specify "followed by the type (4)" do
223
+ @frame[2..3].should == [0,4]
224
+ end
225
+ specify "followed by flags" do
226
+ @frame[4].should == 0
227
+ end
228
+ specify "followed by the length (24 bits)" do
229
+ @frame[5..7].should == [0,0,8]
230
+ end
231
+ specify "followed by the number of entries (32 bits)" do
232
+ @frame[8..11].should == [0,0,0,1]
233
+ end
234
+ specify "followed by ID/Value Pairs (32 bits each)" do
235
+ @frame[12..19].should == [0,0,0,3, 0,0,1,44]
236
+ end
237
+ end
238
+ end
84
239
 
85
- d.to_binary_s.should == DATA
240
+ describe "NOOP" do
241
+ specify "not implemented (being dropped from protocol)" do
242
+ # NOOP
243
+ end
86
244
  end
87
245
 
88
- it "should create a FIN data frame" do
89
- d = SPDY::Protocol::Data::Frame.new
90
- d.create(:stream_id => 1, :flags => 1)
246
+ describe "PING" do
247
+ it "can parse a PING packet" do
248
+ ping = SPDY::Protocol::Control::Ping.new
249
+ ping.parse(PING)
250
+
251
+ ping.ping_id.should == 1
252
+ ping.type.should == 6
253
+
254
+ ping.to_binary_s.should == PING
255
+ end
91
256
 
92
- d.to_binary_s.should == DATA_FIN
257
+ describe "the assembled packet" do
258
+ before do
259
+ @ping = SPDY::Protocol::Control::Ping.new
260
+ @ping.create(:ping_id => 1)
261
+ @frame = Array(@ping.to_binary_s.bytes)
262
+ end
263
+ specify "starts with a control bit" do
264
+ @frame[0].should == 128
265
+ end
266
+ specify "followed by the version (2)" do
267
+ @frame[1].should == 2
268
+ end
269
+ specify "followed by the type (6)" do
270
+ @frame[2..3].should == [0,6]
271
+ end
272
+ specify "followed by flags (0)" do
273
+ @frame[4].should == 0
274
+ end
275
+ specify "followed by the length (always 4)" do
276
+ @frame[5..7].should == [0,0,4]
277
+ end
278
+ end
93
279
  end
94
280
 
95
- it "should read a FIN data frame" do
96
- d = SPDY::Protocol::Data::Frame.new
97
- d.create(:stream_id => 1, :flags => 1)
281
+ describe "GOAWAY" do
282
+ it "can parse a GOAWAY packet" do
283
+ goaway = SPDY::Protocol::Control::Goaway.new
284
+ goaway.parse(GOAWAY)
98
285
 
99
- d.to_binary_s.should == DATA_FIN
100
- pckt = SPDY::Protocol::Data::Frame.new.read(d.to_binary_s)
101
- pckt.flags.should == 1
286
+ goaway.stream_id.should == 1
287
+ goaway.type.should == 7
288
+
289
+ goaway.to_binary_s.should == GOAWAY
290
+ end
291
+
292
+ describe "the assembled packet" do
293
+ before do
294
+ @goaway = SPDY::Protocol::Control::Goaway.new
295
+ @goaway.create(:stream_id => 42)
296
+ @frame = Array(@goaway.to_binary_s.bytes)
297
+ end
298
+ specify "starts with a control bit" do
299
+ @frame[0].should == 128
300
+ end
301
+ specify "followed by the version (2)" do
302
+ @frame[1].should == 2
303
+ end
304
+ specify "followed by the type (7)" do
305
+ @frame[2..3].should == [0,7]
306
+ end
307
+ specify "followed by flags (0)" do
308
+ @frame[4].should == 0
309
+ end
310
+ specify "followed by the length (always 4)" do
311
+ @frame[5..7].should == [0,0,4]
312
+ end
313
+ specify "followed by the last good stream ID (1 ignored bit + 31 bits)" do
314
+ @frame[8..11].should == [0,0,0,42]
315
+ end
316
+ end
102
317
  end
103
318
 
104
- end
319
+ describe "HEADERS" do
320
+ it "can parse a HEADERS packet"do
321
+ zlib_session = SPDY::Zlib.new
322
+
323
+ headers = SPDY::Protocol::Control::Headers.new({:zlib_session => zlib_session})
324
+ headers.parse(HEADERS)
325
+
326
+ headers.header.stream_id.should == 1
327
+ headers.header.type.should == 8
328
+
329
+ headers.to_binary_s.should == HEADERS
330
+ end
331
+
332
+ describe "the assembled packet" do
333
+ before do
334
+ zlib_session = SPDY::Zlib.new
335
+
336
+ @headers = SPDY::Protocol::Control::Headers.new({:zlib_session => zlib_session})
105
337
 
106
- # context "RST_STREAM" do
107
- # it "should parse reset packet"
108
- # end
338
+ nv = {'Content-Type' => 'text/plain', 'status' => '200 OK', 'version' => 'HTTP/1.1'}
339
+ @headers.create({:stream_id => 42, :headers => nv})
340
+
341
+ @frame = Array(@headers.to_binary_s.bytes)
342
+ end
343
+ specify "starts with a control bit" do
344
+ @frame[0].should == 128
345
+ end
346
+ specify "followed by the version (2)" do
347
+ @frame[1].should == 2
348
+ end
349
+ specify "followed by the type (8)" do
350
+ @frame[2..3].should == [0,8]
351
+ end
352
+ specify "followed by flags (8 bits)" do
353
+ @frame[4].should == 0
354
+ end
355
+ specify "followed by the length (24 bits)" do
356
+ # 4 bytes (stream ID)
357
+ # 2 bytes (unused)
358
+ # N bytes for compressed NV section
359
+ @frame[5..7].should == [0,0,53]
360
+ end
361
+ specify "followed by the stream ID (1 ignored bit + 31 bits)" do
362
+ @frame[8..11].should == [0,0,0,42]
363
+ end
364
+ specify "followed by 16 unused bits" do
365
+ @frame[12..13].should == [0,0]
366
+ end
367
+ specify "followed by name/value pairs" do
368
+ @frame[14..-1].size.should == 47
369
+ end
370
+ end
371
+ end
372
+
373
+ describe "NV" do
374
+ describe "creating a packet" do
375
+ before do
376
+ nv = SPDY::Protocol::NV.new
377
+
378
+ @name_values = {'version' => 'HTTP/1.1', 'status' => '200 OK', 'Content-Type' => 'text/plain'}
379
+ nv.create(@name_values)
380
+
381
+ @binary_string = nv.to_binary_s
382
+ end
383
+
384
+ it "begins with the number of name-value pairs" do
385
+ @binary_string[0..1].should == "\x00\x03"
386
+ end
387
+
388
+ it "prefaces names with the length of the name" do
389
+ @binary_string.should =~ %r{\x00\x0cContent-Type}
390
+ end
391
+ it "prefaces values with the length of the value" do
392
+ @binary_string.should =~ %r{\x00\x08HTTP/1.1}
393
+ end
394
+
395
+ it "has 2 bytes (total number of name-value pairs) + 2 bytes for each name (length of name) + 2 bytes for each value (length of value) + names + values" do
396
+ num_size_bytes = 2 + @name_values.size * (2 + 2)
397
+
398
+ @binary_string.length.should ==
399
+ @name_values.inject(num_size_bytes) {|sum, kv| sum + kv[0].length + kv[1].length}
400
+ end
401
+ end
402
+ end
403
+ end
109
404
  end
metadata CHANGED
@@ -1,61 +1,56 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: spdy
3
- version: !ruby/object:Gem::Version
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.3
4
5
  prerelease:
5
- version: 0.0.2
6
6
  platform: ruby
7
- authors:
7
+ authors:
8
8
  - Ilya Grigorik
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
-
13
- date: 2011-04-23 00:00:00 -04:00
14
- default_executable:
15
- dependencies:
16
- - !ruby/object:Gem::Dependency
12
+ date: 2011-10-06 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
17
15
  name: bindata
18
- prerelease: false
19
- requirement: &id001 !ruby/object:Gem::Requirement
16
+ requirement: &2157355140 !ruby/object:Gem::Requirement
20
17
  none: false
21
- requirements:
22
- - - ">="
23
- - !ruby/object:Gem::Version
24
- version: "0"
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
25
22
  type: :runtime
26
- version_requirements: *id001
27
- - !ruby/object:Gem::Dependency
28
- name: ffi-zlib
29
23
  prerelease: false
30
- requirement: &id002 !ruby/object:Gem::Requirement
24
+ version_requirements: *2157355140
25
+ - !ruby/object:Gem::Dependency
26
+ name: ffi-zlib
27
+ requirement: &2157354720 !ruby/object:Gem::Requirement
31
28
  none: false
32
- requirements:
33
- - - ">="
34
- - !ruby/object:Gem::Version
35
- version: "0"
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
36
33
  type: :runtime
37
- version_requirements: *id002
38
- - !ruby/object:Gem::Dependency
39
- name: rspec
40
34
  prerelease: false
41
- requirement: &id003 !ruby/object:Gem::Requirement
35
+ version_requirements: *2157354720
36
+ - !ruby/object:Gem::Dependency
37
+ name: rspec
38
+ requirement: &2157354300 !ruby/object:Gem::Requirement
42
39
  none: false
43
- requirements:
44
- - - ">="
45
- - !ruby/object:Gem::Version
46
- version: "0"
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
47
44
  type: :development
48
- version_requirements: *id003
45
+ prerelease: false
46
+ version_requirements: *2157354300
49
47
  description: SPDY is an experiment with protocols for the web
50
- email:
48
+ email:
51
49
  - ilya@igvita.com
52
50
  executables: []
53
-
54
51
  extensions: []
55
-
56
52
  extra_rdoc_files: []
57
-
58
- files:
53
+ files:
59
54
  - .gitignore
60
55
  - .rspec
61
56
  - Gemfile
@@ -63,6 +58,7 @@ files:
63
58
  - Rakefile
64
59
  - examples/spdy_server.rb
65
60
  - lib/spdy.rb
61
+ - lib/spdy/compat.rb
66
62
  - lib/spdy/compressor.rb
67
63
  - lib/spdy/parser.rb
68
64
  - lib/spdy/protocol.rb
@@ -72,35 +68,31 @@ files:
72
68
  - spec/helper.rb
73
69
  - spec/parser_spec.rb
74
70
  - spec/protocol_spec.rb
75
- has_rdoc: true
76
71
  homepage: https://github.com/igrigorik/spdy
77
72
  licenses: []
78
-
79
73
  post_install_message:
80
74
  rdoc_options: []
81
-
82
- require_paths:
75
+ require_paths:
83
76
  - lib
84
- required_ruby_version: !ruby/object:Gem::Requirement
77
+ required_ruby_version: !ruby/object:Gem::Requirement
85
78
  none: false
86
- requirements:
87
- - - ">="
88
- - !ruby/object:Gem::Version
89
- version: "0"
90
- required_rubygems_version: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ! '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ required_rubygems_version: !ruby/object:Gem::Requirement
91
84
  none: false
92
- requirements:
93
- - - ">="
94
- - !ruby/object:Gem::Version
95
- version: "0"
85
+ requirements:
86
+ - - ! '>='
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
96
89
  requirements: []
97
-
98
90
  rubyforge_project: spdy
99
- rubygems_version: 1.6.2
91
+ rubygems_version: 1.8.5
100
92
  signing_key:
101
93
  specification_version: 3
102
94
  summary: SPDY is an experiment with protocols for the web
103
- test_files:
95
+ test_files:
104
96
  - spec/compressor_spec.rb
105
97
  - spec/helper.rb
106
98
  - spec/parser_spec.rb